- On January 1, 2020 this library will no longer support Python 2 on the latest released version. - Previously released library versions will continue to be available. For more information please + As of January 1, 2020 this library no longer supports Python 2 on the latest released version. + Library versions released prior to that date will continue to be available. For more information please visit Python 2 support on Google Cloud.
{% block body %} {% endblock %} diff --git a/docs/conf.py b/docs/conf.py index 432d32f..320e5cd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,12 +20,16 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath("..")) +# For plugins that can not read conf.py. +# See also: https://github.com/docascode/sphinx-docfx-yaml/issues/85 +sys.path.insert(0, os.path.abspath(".")) + __version__ = "" # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = "1.6.3" +needs_sphinx = "1.5.5" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -35,24 +39,22 @@ "sphinx.ext.autosummary", "sphinx.ext.intersphinx", "sphinx.ext.coverage", + "sphinx.ext.doctest", "sphinx.ext.napoleon", "sphinx.ext.todo", "sphinx.ext.viewcode", + "recommonmark", ] # autodoc/autosummary flags autoclass_content = "both" -autodoc_default_flags = ["members"] +autodoc_default_options = {"members": True} autosummary_generate = True # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] -# Allow markdown includes (so releases.md can include CHANGLEOG.md) -# http://www.sphinx-doc.org/en/master/markdown.html -source_parsers = {".md": "recommonmark.parser.CommonMarkParser"} - # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] @@ -93,7 +95,12 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ["_build"] +exclude_patterns = [ + "_build", + "samples/AUTHORING_GUIDE.md", + "samples/CONTRIBUTING.md", + "samples/snippets/README.rst", +] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -338,14 +345,11 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - "python": ("http://python.readthedocs.org/en/latest/", None), - "google-auth": ("https://google-auth.readthedocs.io/en/stable", None), - "google.api_core": ( - "https://googleapis.dev/python/google-api-core/latest/", - None, - ), - "grpc": ("https://grpc.io/grpc/python/", None), - + "python": ("https://python.readthedocs.org/en/latest/", None), + "google-auth": ("https://googleapis.dev/python/google-auth/latest/", None), + "google.api_core": ("https://googleapis.dev/python/google-api-core/latest/", None,), + "grpc": ("https://grpc.github.io/grpc/python/", None), + "proto-plus": ("https://proto-plus-python.readthedocs.io/en/latest/", None), } diff --git a/docs/index.rst b/docs/index.rst index c72130c..7be83ed 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,11 +1,16 @@ -.. include:: README.md +.. include:: README.rst + +.. include:: multiprocessing.rst + +API Reference +------------- .. toctree:: :maxdepth: 2 - v1 + orgpolicy_v2/services + orgpolicy_v2/types -.. include:: multiprocessing.rst Changelog --------- @@ -13,6 +18,6 @@ Changelog For a list of all ``google-cloud-org-policy`` releases: .. toctree:: - :maxdepth: 2 + :maxdepth: 2 - changelog \ No newline at end of file + changelog \ No newline at end of file diff --git a/docs/orgpolicy_v2/org_policy.rst b/docs/orgpolicy_v2/org_policy.rst new file mode 100644 index 0000000..18c078a --- /dev/null +++ b/docs/orgpolicy_v2/org_policy.rst @@ -0,0 +1,11 @@ +OrgPolicy +--------------------------- + +.. automodule:: google.cloud.orgpolicy_v2.services.org_policy + :members: + :inherited-members: + + +.. automodule:: google.cloud.orgpolicy_v2.services.org_policy.pagers + :members: + :inherited-members: diff --git a/docs/orgpolicy_v2/services.rst b/docs/orgpolicy_v2/services.rst new file mode 100644 index 0000000..2442b5b --- /dev/null +++ b/docs/orgpolicy_v2/services.rst @@ -0,0 +1,6 @@ +Services for Google Cloud Orgpolicy v2 API +========================================== +.. toctree:: + :maxdepth: 2 + + org_policy diff --git a/docs/orgpolicy_v2/types.rst b/docs/orgpolicy_v2/types.rst new file mode 100644 index 0000000..12a88ff --- /dev/null +++ b/docs/orgpolicy_v2/types.rst @@ -0,0 +1,7 @@ +Types for Google Cloud Orgpolicy v2 API +======================================= + +.. automodule:: google.cloud.orgpolicy_v2.types + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/v1.rst b/docs/v1.rst deleted file mode 100644 index 6177971..0000000 --- a/docs/v1.rst +++ /dev/null @@ -1,6 +0,0 @@ -google.cloud.orgpolicy.v1 -================================ - -.. automodule:: google.cloud.orgpolicy.v1.orgpolicy_pb2 - :members: - :inherited-members: \ No newline at end of file diff --git a/google/cloud/orgpolicy/__init__.py b/google/cloud/orgpolicy/__init__.py index e69de29..0cce5a0 100644 --- a/google/cloud/orgpolicy/__init__.py +++ b/google/cloud/orgpolicy/__init__.py @@ -0,0 +1,52 @@ +# -*- 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.orgpolicy_v2.services.org_policy.async_client import ( + OrgPolicyAsyncClient, +) +from google.cloud.orgpolicy_v2.services.org_policy.client import OrgPolicyClient +from google.cloud.orgpolicy_v2.types.constraint import Constraint +from google.cloud.orgpolicy_v2.types.orgpolicy import AlternatePolicySpec +from google.cloud.orgpolicy_v2.types.orgpolicy import CreatePolicyRequest +from google.cloud.orgpolicy_v2.types.orgpolicy import DeletePolicyRequest +from google.cloud.orgpolicy_v2.types.orgpolicy import GetEffectivePolicyRequest +from google.cloud.orgpolicy_v2.types.orgpolicy import GetPolicyRequest +from google.cloud.orgpolicy_v2.types.orgpolicy import ListConstraintsRequest +from google.cloud.orgpolicy_v2.types.orgpolicy import ListConstraintsResponse +from google.cloud.orgpolicy_v2.types.orgpolicy import ListPoliciesRequest +from google.cloud.orgpolicy_v2.types.orgpolicy import ListPoliciesResponse +from google.cloud.orgpolicy_v2.types.orgpolicy import Policy +from google.cloud.orgpolicy_v2.types.orgpolicy import PolicySpec +from google.cloud.orgpolicy_v2.types.orgpolicy import UpdatePolicyRequest + +__all__ = ( + "AlternatePolicySpec", + "Constraint", + "CreatePolicyRequest", + "DeletePolicyRequest", + "GetEffectivePolicyRequest", + "GetPolicyRequest", + "ListConstraintsRequest", + "ListConstraintsResponse", + "ListPoliciesRequest", + "ListPoliciesResponse", + "OrgPolicyAsyncClient", + "OrgPolicyClient", + "Policy", + "PolicySpec", + "UpdatePolicyRequest", +) diff --git a/google/cloud/orgpolicy/py.typed b/google/cloud/orgpolicy/py.typed new file mode 100644 index 0000000..3ef785e --- /dev/null +++ b/google/cloud/orgpolicy/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-cloud-org-policy package uses inline types. diff --git a/google/cloud/orgpolicy/v1/__init__.py b/google/cloud/orgpolicy/v1/__init__.py index c3eca03..8d4431e 100644 --- a/google/cloud/orgpolicy/v1/__init__.py +++ b/google/cloud/orgpolicy/v1/__init__.py @@ -16,7 +16,6 @@ from __future__ import absolute_import -import sys from google.api_core.protobuf_helpers import get_messages diff --git a/google/cloud/orgpolicy_v2/__init__.py b/google/cloud/orgpolicy_v2/__init__.py new file mode 100644 index 0000000..35f8011 --- /dev/null +++ b/google/cloud/orgpolicy_v2/__init__.py @@ -0,0 +1,49 @@ +# -*- 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.org_policy import OrgPolicyClient +from .types.constraint import Constraint +from .types.orgpolicy import AlternatePolicySpec +from .types.orgpolicy import CreatePolicyRequest +from .types.orgpolicy import DeletePolicyRequest +from .types.orgpolicy import GetEffectivePolicyRequest +from .types.orgpolicy import GetPolicyRequest +from .types.orgpolicy import ListConstraintsRequest +from .types.orgpolicy import ListConstraintsResponse +from .types.orgpolicy import ListPoliciesRequest +from .types.orgpolicy import ListPoliciesResponse +from .types.orgpolicy import Policy +from .types.orgpolicy import PolicySpec +from .types.orgpolicy import UpdatePolicyRequest + + +__all__ = ( + "AlternatePolicySpec", + "Constraint", + "CreatePolicyRequest", + "DeletePolicyRequest", + "GetEffectivePolicyRequest", + "GetPolicyRequest", + "ListConstraintsRequest", + "ListConstraintsResponse", + "ListPoliciesRequest", + "ListPoliciesResponse", + "Policy", + "PolicySpec", + "UpdatePolicyRequest", + "OrgPolicyClient", +) diff --git a/google/cloud/orgpolicy_v2/py.typed b/google/cloud/orgpolicy_v2/py.typed new file mode 100644 index 0000000..3ef785e --- /dev/null +++ b/google/cloud/orgpolicy_v2/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-cloud-org-policy package uses inline types. diff --git a/google/cloud/orgpolicy_v2/services/__init__.py b/google/cloud/orgpolicy_v2/services/__init__.py new file mode 100644 index 0000000..42ffdf2 --- /dev/null +++ b/google/cloud/orgpolicy_v2/services/__init__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/google/cloud/orgpolicy_v2/services/org_policy/__init__.py b/google/cloud/orgpolicy_v2/services/org_policy/__init__.py new file mode 100644 index 0000000..7f5da48 --- /dev/null +++ b/google/cloud/orgpolicy_v2/services/org_policy/__init__.py @@ -0,0 +1,24 @@ +# -*- 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 OrgPolicyClient +from .async_client import OrgPolicyAsyncClient + +__all__ = ( + "OrgPolicyClient", + "OrgPolicyAsyncClient", +) diff --git a/google/cloud/orgpolicy_v2/services/org_policy/async_client.py b/google/cloud/orgpolicy_v2/services/org_policy/async_client.py new file mode 100644 index 0000000..4c14d45 --- /dev/null +++ b/google/cloud/orgpolicy_v2/services/org_policy/async_client.py @@ -0,0 +1,821 @@ +# -*- 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 # 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 # type: ignore +from google.oauth2 import service_account # type: ignore + +from google.cloud.orgpolicy_v2.services.org_policy import pagers +from google.cloud.orgpolicy_v2.types import constraint +from google.cloud.orgpolicy_v2.types import orgpolicy + +from .transports.base import OrgPolicyTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import OrgPolicyGrpcAsyncIOTransport +from .client import OrgPolicyClient + + +class OrgPolicyAsyncClient: + """An interface for managing organization policies. + + The Cloud Org Policy service provides a simple mechanism for + organizations to restrict the allowed configurations across their + entire Cloud Resource hierarchy. + + You can use a ``policy`` to configure restrictions in Cloud + resources. For example, you can enforce a ``policy`` that restricts + which Google Cloud Platform APIs can be activated in a certain part + of your resource hierarchy, or prevents serial port access to VM + instances in a particular folder. + + ``Policies`` are inherited down through the resource hierarchy. A + ``policy`` applied to a parent resource automatically applies to all + its child resources unless overridden with a ``policy`` lower in the + hierarchy. + + A ``constraint`` defines an aspect of a resource's configuration + that can be controlled by an organization's policy administrator. + ``Policies`` are a collection of ``constraints`` that defines their + allowable configuration on a particular resource and its child + resources. + """ + + _client: OrgPolicyClient + + DEFAULT_ENDPOINT = OrgPolicyClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = OrgPolicyClient.DEFAULT_MTLS_ENDPOINT + + constraint_path = staticmethod(OrgPolicyClient.constraint_path) + parse_constraint_path = staticmethod(OrgPolicyClient.parse_constraint_path) + policy_path = staticmethod(OrgPolicyClient.policy_path) + parse_policy_path = staticmethod(OrgPolicyClient.parse_policy_path) + + common_billing_account_path = staticmethod( + OrgPolicyClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + OrgPolicyClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(OrgPolicyClient.common_folder_path) + parse_common_folder_path = staticmethod(OrgPolicyClient.parse_common_folder_path) + + common_organization_path = staticmethod(OrgPolicyClient.common_organization_path) + parse_common_organization_path = staticmethod( + OrgPolicyClient.parse_common_organization_path + ) + + common_project_path = staticmethod(OrgPolicyClient.common_project_path) + parse_common_project_path = staticmethod(OrgPolicyClient.parse_common_project_path) + + common_location_path = staticmethod(OrgPolicyClient.common_location_path) + parse_common_location_path = staticmethod( + OrgPolicyClient.parse_common_location_path + ) + + from_service_account_info = OrgPolicyClient.from_service_account_info + from_service_account_file = OrgPolicyClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> OrgPolicyTransport: + """Return the transport used by the client instance. + + Returns: + OrgPolicyTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(OrgPolicyClient).get_transport_class, type(OrgPolicyClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, OrgPolicyTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the org policy 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, ~.OrgPolicyTransport]): 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 = OrgPolicyClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_constraints( + self, + request: orgpolicy.ListConstraintsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListConstraintsAsyncPager: + r"""Lists ``Constraints`` that could be applied on the specified + resource. + + Args: + request (:class:`google.cloud.orgpolicy_v2.types.ListConstraintsRequest`): + The request object. The request sent to the + [ListConstraints] + [google.cloud.orgpolicy.v2.OrgPolicy.ListConstraints] + method. + parent (:class:`str`): + Required. The Cloud resource that parents the + constraint. Must be in one of the following forms: + + - ``projects/{project_number}`` + - ``projects/{project_id}`` + - ``folders/{folder_id}`` + - ``organizations/{organization_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.orgpolicy_v2.services.org_policy.pagers.ListConstraintsAsyncPager: + The response returned from the [ListConstraints] + [google.cloud.orgpolicy.v2.OrgPolicy.ListConstraints] + 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([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 = orgpolicy.ListConstraintsRequest(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_constraints, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + 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((("parent", request.parent),)), + ) + + # 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.ListConstraintsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def list_policies( + self, + request: orgpolicy.ListPoliciesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListPoliciesAsyncPager: + r"""Retrieves all of the ``Policies`` that exist on a particular + resource. + + Args: + request (:class:`google.cloud.orgpolicy_v2.types.ListPoliciesRequest`): + The request object. The request sent to the + [ListPolicies] + [google.cloud.orgpolicy.v2.OrgPolicy.ListPolicies] + method. + parent (:class:`str`): + Required. The target Cloud resource that parents the set + of constraints and policies that will be returned from + this call. Must be in one of the following forms: + + - ``projects/{project_number}`` + - ``projects/{project_id}`` + - ``folders/{folder_id}`` + - ``organizations/{organization_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.orgpolicy_v2.services.org_policy.pagers.ListPoliciesAsyncPager: + The response returned from the [ListPolicies] + [google.cloud.orgpolicy.v2.OrgPolicy.ListPolicies] + method. It will be empty if no Policies are set on + the resource. + + 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 = orgpolicy.ListPoliciesRequest(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_policies, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + 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((("parent", request.parent),)), + ) + + # 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.ListPoliciesAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_policy( + self, + request: orgpolicy.GetPolicyRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> orgpolicy.Policy: + r"""Gets a ``Policy`` on a resource. + + If no ``Policy`` is set on the resource, NOT_FOUND is returned. + The ``etag`` value can be used with ``UpdatePolicy()`` to update + a ``Policy`` during read-modify-write. + + Args: + request (:class:`google.cloud.orgpolicy_v2.types.GetPolicyRequest`): + The request object. The request sent to the [GetPolicy] + [google.cloud.orgpolicy.v2.OrgPolicy.GetPolicy] method. + name (:class:`str`): + Required. Resource name of the policy. See ``Policy`` + for naming requirements. + + 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.orgpolicy_v2.types.Policy: + Defines a Cloud Organization Policy which is used to specify Constraints + for configurations of 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 = orgpolicy.GetPolicyRequest(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_policy, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + 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 get_effective_policy( + self, + request: orgpolicy.GetEffectivePolicyRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> orgpolicy.Policy: + r"""Gets the effective ``Policy`` on a resource. This is the result + of merging ``Policies`` in the resource hierarchy and evaluating + conditions. The returned ``Policy`` will not have an ``etag`` or + ``condition`` set because it is a computed ``Policy`` across + multiple resources. Subtrees of Resource Manager resource + hierarchy with 'under:' prefix will not be expanded. + + Args: + request (:class:`google.cloud.orgpolicy_v2.types.GetEffectivePolicyRequest`): + The request object. The request sent to the + [GetEffectivePolicy] + [google.cloud.orgpolicy.v2.OrgPolicy.GetEffectivePolicy] + method. + name (:class:`str`): + Required. The effective policy to compute. See + ``Policy`` for naming rules. + + 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.orgpolicy_v2.types.Policy: + Defines a Cloud Organization Policy which is used to specify Constraints + for configurations of 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 = orgpolicy.GetEffectivePolicyRequest(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_effective_policy, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + 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_policy( + self, + request: orgpolicy.CreatePolicyRequest = None, + *, + parent: str = None, + policy: orgpolicy.Policy = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> orgpolicy.Policy: + r"""Creates a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint does not exist. + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.ALREADY_EXISTS`` if the policy already exists + on the given Cloud resource. + + Args: + request (:class:`google.cloud.orgpolicy_v2.types.CreatePolicyRequest`): + The request object. The request sent to the + [CreatePolicyRequest] + [google.cloud.orgpolicy.v2.OrgPolicy.CreatePolicy] + method. + parent (:class:`str`): + Required. The Cloud resource that will parent the new + Policy. Must be in one of the following forms: + + - ``projects/{project_number}`` + - ``projects/{project_id}`` + - ``folders/{folder_id}`` + - ``organizations/{organization_id}`` + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + policy (:class:`google.cloud.orgpolicy_v2.types.Policy`): + Required. ``Policy`` to create. + This corresponds to the ``policy`` 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.orgpolicy_v2.types.Policy: + Defines a Cloud Organization Policy which is used to specify Constraints + for configurations of 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([parent, policy]) + 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 = orgpolicy.CreatePolicyRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if policy is not None: + request.policy = policy + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.create_policy, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + 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((("parent", request.parent),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def update_policy( + self, + request: orgpolicy.UpdatePolicyRequest = None, + *, + policy: orgpolicy.Policy = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> orgpolicy.Policy: + r"""Updates a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint or the policy do + not exist. Returns a ``google.rpc.Status`` with + ``google.rpc.Code.ABORTED`` if the etag supplied in the request + does not match the persisted etag of the policy + + Note: the supplied policy will perform a full overwrite of all + fields. + + Args: + request (:class:`google.cloud.orgpolicy_v2.types.UpdatePolicyRequest`): + The request object. The request sent to the + [UpdatePolicyRequest] + [google.cloud.orgpolicy.v2.OrgPolicy.UpdatePolicy] + method. + policy (:class:`google.cloud.orgpolicy_v2.types.Policy`): + Required. ``Policy`` to update. + This corresponds to the ``policy`` 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.orgpolicy_v2.types.Policy: + Defines a Cloud Organization Policy which is used to specify Constraints + for configurations of 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([policy]) + 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 = orgpolicy.UpdatePolicyRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if policy is not None: + request.policy = policy + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_policy, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + 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( + (("policy.name", request.policy.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_policy( + self, + request: orgpolicy.DeletePolicyRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint or Org Policy + does not exist. + + Args: + request (:class:`google.cloud.orgpolicy_v2.types.DeletePolicyRequest`): + The request object. The request sent to the + [DeletePolicy] + [google.cloud.orgpolicy.v2.OrgPolicy.DeletePolicy] + method. + name (:class:`str`): + Required. Name of the policy to delete. See ``Policy`` + for naming rules. + + 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. + """ + # 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 = orgpolicy.DeletePolicyRequest(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_policy, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + 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. + await rpc( + request, retry=retry, timeout=timeout, metadata=metadata, + ) + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-org-policy", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("OrgPolicyAsyncClient",) diff --git a/google/cloud/orgpolicy_v2/services/org_policy/client.py b/google/cloud/orgpolicy_v2/services/org_policy/client.py new file mode 100644 index 0000000..276a12d --- /dev/null +++ b/google/cloud/orgpolicy_v2/services/org_policy/client.py @@ -0,0 +1,988 @@ +# -*- 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 # 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 # 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.orgpolicy_v2.services.org_policy import pagers +from google.cloud.orgpolicy_v2.types import constraint +from google.cloud.orgpolicy_v2.types import orgpolicy + +from .transports.base import OrgPolicyTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import OrgPolicyGrpcTransport +from .transports.grpc_asyncio import OrgPolicyGrpcAsyncIOTransport + + +class OrgPolicyClientMeta(type): + """Metaclass for the OrgPolicy 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[OrgPolicyTransport]] + _transport_registry["grpc"] = OrgPolicyGrpcTransport + _transport_registry["grpc_asyncio"] = OrgPolicyGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[OrgPolicyTransport]: + """Return 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 OrgPolicyClient(metaclass=OrgPolicyClientMeta): + """An interface for managing organization policies. + + The Cloud Org Policy service provides a simple mechanism for + organizations to restrict the allowed configurations across their + entire Cloud Resource hierarchy. + + You can use a ``policy`` to configure restrictions in Cloud + resources. For example, you can enforce a ``policy`` that restricts + which Google Cloud Platform APIs can be activated in a certain part + of your resource hierarchy, or prevents serial port access to VM + instances in a particular folder. + + ``Policies`` are inherited down through the resource hierarchy. A + ``policy`` applied to a parent resource automatically applies to all + its child resources unless overridden with a ``policy`` lower in the + hierarchy. + + A ``constraint`` defines an aspect of a resource's configuration + that can be controlled by an organization's policy administrator. + ``Policies`` are a collection of ``constraints`` that defines their + allowable configuration on a particular resource and its child + resources. + """ + + @staticmethod + def _get_default_mtls_endpoint(api_endpoint): + """Convert 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 = "orgpolicy.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: + OrgPolicyClient: 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: + OrgPolicyClient: 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) -> OrgPolicyTransport: + """Return the transport used by the client instance. + + Returns: + OrgPolicyTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def constraint_path(project: str, constraint: str,) -> str: + """Return a fully-qualified constraint string.""" + return "projects/{project}/constraints/{constraint}".format( + project=project, constraint=constraint, + ) + + @staticmethod + def parse_constraint_path(path: str) -> Dict[str, str]: + """Parse a constraint path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/constraints/(?P.+?)$", path + ) + return m.groupdict() if m else {} + + @staticmethod + def policy_path(project: str, policy: str,) -> str: + """Return a fully-qualified policy string.""" + return "projects/{project}/policies/{policy}".format( + project=project, policy=policy, + ) + + @staticmethod + def parse_policy_path(path: str) -> Dict[str, str]: + """Parse a policy path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/policies/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def common_billing_account_path(billing_account: str,) -> str: + """Return 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: + """Return 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: + """Return 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: + """Return 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: + """Return 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[credentials.Credentials] = None, + transport: Union[str, OrgPolicyTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the org policy 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, OrgPolicyTransport]): 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() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + 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": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else 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, OrgPolicyTransport): + # transport is a OrgPolicyTransport 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_constraints( + self, + request: orgpolicy.ListConstraintsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListConstraintsPager: + r"""Lists ``Constraints`` that could be applied on the specified + resource. + + Args: + request (google.cloud.orgpolicy_v2.types.ListConstraintsRequest): + The request object. The request sent to the + [ListConstraints] + [google.cloud.orgpolicy.v2.OrgPolicy.ListConstraints] + method. + parent (str): + Required. The Cloud resource that parents the + constraint. Must be in one of the following forms: + + - ``projects/{project_number}`` + - ``projects/{project_id}`` + - ``folders/{folder_id}`` + - ``organizations/{organization_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.orgpolicy_v2.services.org_policy.pagers.ListConstraintsPager: + The response returned from the [ListConstraints] + [google.cloud.orgpolicy.v2.OrgPolicy.ListConstraints] + 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([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 orgpolicy.ListConstraintsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, orgpolicy.ListConstraintsRequest): + request = orgpolicy.ListConstraintsRequest(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_constraints] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), + ) + + # 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.ListConstraintsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def list_policies( + self, + request: orgpolicy.ListPoliciesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListPoliciesPager: + r"""Retrieves all of the ``Policies`` that exist on a particular + resource. + + Args: + request (google.cloud.orgpolicy_v2.types.ListPoliciesRequest): + The request object. The request sent to the + [ListPolicies] + [google.cloud.orgpolicy.v2.OrgPolicy.ListPolicies] + method. + parent (str): + Required. The target Cloud resource that parents the set + of constraints and policies that will be returned from + this call. Must be in one of the following forms: + + - ``projects/{project_number}`` + - ``projects/{project_id}`` + - ``folders/{folder_id}`` + - ``organizations/{organization_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.orgpolicy_v2.services.org_policy.pagers.ListPoliciesPager: + The response returned from the [ListPolicies] + [google.cloud.orgpolicy.v2.OrgPolicy.ListPolicies] + method. It will be empty if no Policies are set on + the resource. + + 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 orgpolicy.ListPoliciesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, orgpolicy.ListPoliciesRequest): + request = orgpolicy.ListPoliciesRequest(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_policies] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), + ) + + # 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.ListPoliciesPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_policy( + self, + request: orgpolicy.GetPolicyRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> orgpolicy.Policy: + r"""Gets a ``Policy`` on a resource. + + If no ``Policy`` is set on the resource, NOT_FOUND is returned. + The ``etag`` value can be used with ``UpdatePolicy()`` to update + a ``Policy`` during read-modify-write. + + Args: + request (google.cloud.orgpolicy_v2.types.GetPolicyRequest): + The request object. The request sent to the [GetPolicy] + [google.cloud.orgpolicy.v2.OrgPolicy.GetPolicy] method. + name (str): + Required. Resource name of the policy. See ``Policy`` + for naming requirements. + + 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.orgpolicy_v2.types.Policy: + Defines a Cloud Organization Policy which is used to specify Constraints + for configurations of 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 orgpolicy.GetPolicyRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, orgpolicy.GetPolicyRequest): + request = orgpolicy.GetPolicyRequest(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_policy] + + # 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 get_effective_policy( + self, + request: orgpolicy.GetEffectivePolicyRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> orgpolicy.Policy: + r"""Gets the effective ``Policy`` on a resource. This is the result + of merging ``Policies`` in the resource hierarchy and evaluating + conditions. The returned ``Policy`` will not have an ``etag`` or + ``condition`` set because it is a computed ``Policy`` across + multiple resources. Subtrees of Resource Manager resource + hierarchy with 'under:' prefix will not be expanded. + + Args: + request (google.cloud.orgpolicy_v2.types.GetEffectivePolicyRequest): + The request object. The request sent to the + [GetEffectivePolicy] + [google.cloud.orgpolicy.v2.OrgPolicy.GetEffectivePolicy] + method. + name (str): + Required. The effective policy to compute. See + ``Policy`` for naming rules. + + 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.orgpolicy_v2.types.Policy: + Defines a Cloud Organization Policy which is used to specify Constraints + for configurations of 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 orgpolicy.GetEffectivePolicyRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, orgpolicy.GetEffectivePolicyRequest): + request = orgpolicy.GetEffectivePolicyRequest(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_effective_policy] + + # 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_policy( + self, + request: orgpolicy.CreatePolicyRequest = None, + *, + parent: str = None, + policy: orgpolicy.Policy = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> orgpolicy.Policy: + r"""Creates a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint does not exist. + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.ALREADY_EXISTS`` if the policy already exists + on the given Cloud resource. + + Args: + request (google.cloud.orgpolicy_v2.types.CreatePolicyRequest): + The request object. The request sent to the + [CreatePolicyRequest] + [google.cloud.orgpolicy.v2.OrgPolicy.CreatePolicy] + method. + parent (str): + Required. The Cloud resource that will parent the new + Policy. Must be in one of the following forms: + + - ``projects/{project_number}`` + - ``projects/{project_id}`` + - ``folders/{folder_id}`` + - ``organizations/{organization_id}`` + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + policy (google.cloud.orgpolicy_v2.types.Policy): + Required. ``Policy`` to create. + This corresponds to the ``policy`` 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.orgpolicy_v2.types.Policy: + Defines a Cloud Organization Policy which is used to specify Constraints + for configurations of 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([parent, policy]) + 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 orgpolicy.CreatePolicyRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, orgpolicy.CreatePolicyRequest): + request = orgpolicy.CreatePolicyRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if policy is not None: + request.policy = policy + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def update_policy( + self, + request: orgpolicy.UpdatePolicyRequest = None, + *, + policy: orgpolicy.Policy = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> orgpolicy.Policy: + r"""Updates a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint or the policy do + not exist. Returns a ``google.rpc.Status`` with + ``google.rpc.Code.ABORTED`` if the etag supplied in the request + does not match the persisted etag of the policy + + Note: the supplied policy will perform a full overwrite of all + fields. + + Args: + request (google.cloud.orgpolicy_v2.types.UpdatePolicyRequest): + The request object. The request sent to the + [UpdatePolicyRequest] + [google.cloud.orgpolicy.v2.OrgPolicy.UpdatePolicy] + method. + policy (google.cloud.orgpolicy_v2.types.Policy): + Required. ``Policy`` to update. + This corresponds to the ``policy`` 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.orgpolicy_v2.types.Policy: + Defines a Cloud Organization Policy which is used to specify Constraints + for configurations of 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([policy]) + 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 orgpolicy.UpdatePolicyRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, orgpolicy.UpdatePolicyRequest): + request = orgpolicy.UpdatePolicyRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if policy is not None: + request.policy = policy + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_policy] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("policy.name", request.policy.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def delete_policy( + self, + request: orgpolicy.DeletePolicyRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint or Org Policy + does not exist. + + Args: + request (google.cloud.orgpolicy_v2.types.DeletePolicyRequest): + The request object. The request sent to the + [DeletePolicy] + [google.cloud.orgpolicy.v2.OrgPolicy.DeletePolicy] + method. + name (str): + Required. Name of the policy to delete. See ``Policy`` + for naming rules. + + 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. + """ + # 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 orgpolicy.DeletePolicyRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, orgpolicy.DeletePolicyRequest): + request = orgpolicy.DeletePolicyRequest(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_policy] + + # 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. + rpc( + request, retry=retry, timeout=timeout, metadata=metadata, + ) + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-org-policy", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("OrgPolicyClient",) diff --git a/google/cloud/orgpolicy_v2/services/org_policy/pagers.py b/google/cloud/orgpolicy_v2/services/org_policy/pagers.py new file mode 100644 index 0000000..df568a9 --- /dev/null +++ b/google/cloud/orgpolicy_v2/services/org_policy/pagers.py @@ -0,0 +1,286 @@ +# -*- 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.orgpolicy_v2.types import constraint +from google.cloud.orgpolicy_v2.types import orgpolicy + + +class ListConstraintsPager: + """A pager for iterating through ``list_constraints`` requests. + + This class thinly wraps an initial + :class:`google.cloud.orgpolicy_v2.types.ListConstraintsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``constraints`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListConstraints`` requests and continue to iterate + through the ``constraints`` field on the + corresponding responses. + + All the usual :class:`google.cloud.orgpolicy_v2.types.ListConstraintsResponse` + 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[..., orgpolicy.ListConstraintsResponse], + request: orgpolicy.ListConstraintsRequest, + response: orgpolicy.ListConstraintsResponse, + *, + 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.orgpolicy_v2.types.ListConstraintsRequest): + The initial request object. + response (google.cloud.orgpolicy_v2.types.ListConstraintsResponse): + 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 = orgpolicy.ListConstraintsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[orgpolicy.ListConstraintsResponse]: + 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[constraint.Constraint]: + for page in self.pages: + yield from page.constraints + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListConstraintsAsyncPager: + """A pager for iterating through ``list_constraints`` requests. + + This class thinly wraps an initial + :class:`google.cloud.orgpolicy_v2.types.ListConstraintsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``constraints`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListConstraints`` requests and continue to iterate + through the ``constraints`` field on the + corresponding responses. + + All the usual :class:`google.cloud.orgpolicy_v2.types.ListConstraintsResponse` + 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[orgpolicy.ListConstraintsResponse]], + request: orgpolicy.ListConstraintsRequest, + response: orgpolicy.ListConstraintsResponse, + *, + 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.orgpolicy_v2.types.ListConstraintsRequest): + The initial request object. + response (google.cloud.orgpolicy_v2.types.ListConstraintsResponse): + 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 = orgpolicy.ListConstraintsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[orgpolicy.ListConstraintsResponse]: + 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[constraint.Constraint]: + async def async_generator(): + async for page in self.pages: + for response in page.constraints: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListPoliciesPager: + """A pager for iterating through ``list_policies`` requests. + + This class thinly wraps an initial + :class:`google.cloud.orgpolicy_v2.types.ListPoliciesResponse` object, and + provides an ``__iter__`` method to iterate through its + ``policies`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListPolicies`` requests and continue to iterate + through the ``policies`` field on the + corresponding responses. + + All the usual :class:`google.cloud.orgpolicy_v2.types.ListPoliciesResponse` + 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[..., orgpolicy.ListPoliciesResponse], + request: orgpolicy.ListPoliciesRequest, + response: orgpolicy.ListPoliciesResponse, + *, + 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.orgpolicy_v2.types.ListPoliciesRequest): + The initial request object. + response (google.cloud.orgpolicy_v2.types.ListPoliciesResponse): + 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 = orgpolicy.ListPoliciesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[orgpolicy.ListPoliciesResponse]: + 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[orgpolicy.Policy]: + for page in self.pages: + yield from page.policies + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListPoliciesAsyncPager: + """A pager for iterating through ``list_policies`` requests. + + This class thinly wraps an initial + :class:`google.cloud.orgpolicy_v2.types.ListPoliciesResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``policies`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListPolicies`` requests and continue to iterate + through the ``policies`` field on the + corresponding responses. + + All the usual :class:`google.cloud.orgpolicy_v2.types.ListPoliciesResponse` + 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[orgpolicy.ListPoliciesResponse]], + request: orgpolicy.ListPoliciesRequest, + response: orgpolicy.ListPoliciesResponse, + *, + 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.orgpolicy_v2.types.ListPoliciesRequest): + The initial request object. + response (google.cloud.orgpolicy_v2.types.ListPoliciesResponse): + 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 = orgpolicy.ListPoliciesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[orgpolicy.ListPoliciesResponse]: + 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[orgpolicy.Policy]: + async def async_generator(): + async for page in self.pages: + for response in page.policies: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/orgpolicy_v2/services/org_policy/transports/__init__.py b/google/cloud/orgpolicy_v2/services/org_policy/transports/__init__.py new file mode 100644 index 0000000..0a3112a --- /dev/null +++ b/google/cloud/orgpolicy_v2/services/org_policy/transports/__init__.py @@ -0,0 +1,35 @@ +# -*- 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 OrgPolicyTransport +from .grpc import OrgPolicyGrpcTransport +from .grpc_asyncio import OrgPolicyGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[OrgPolicyTransport]] +_transport_registry["grpc"] = OrgPolicyGrpcTransport +_transport_registry["grpc_asyncio"] = OrgPolicyGrpcAsyncIOTransport + +__all__ = ( + "OrgPolicyTransport", + "OrgPolicyGrpcTransport", + "OrgPolicyGrpcAsyncIOTransport", +) diff --git a/google/cloud/orgpolicy_v2/services/org_policy/transports/base.py b/google/cloud/orgpolicy_v2/services/org_policy/transports/base.py new file mode 100644 index 0000000..d53f3e4 --- /dev/null +++ b/google/cloud/orgpolicy_v2/services/org_policy/transports/base.py @@ -0,0 +1,273 @@ +# -*- 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 +import typing +import pkg_resources + +from google import auth # type: ignore +from google.api_core import 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 # type: ignore + +from google.cloud.orgpolicy_v2.types import orgpolicy +from google.protobuf import empty_pb2 as empty # type: ignore + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-org-policy", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class OrgPolicyTransport(abc.ABC): + """Abstract transport class for OrgPolicy.""" + + AUTH_SCOPES = ("https://www.googleapis.com/auth/cloud-platform",) + + def __init__( + self, + *, + host: str = "orgpolicy.googleapis.com", + credentials: credentials.Credentials = None, + credentials_file: typing.Optional[str] = None, + scopes: typing.Optional[typing.Sequence[str]] = AUTH_SCOPES, + quota_project_id: typing.Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + **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. + scope (Optional[Sequence[str]]): A list of scopes. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + """ + # Save the hostname. Default to port 443 (HTTPS) if none is specified. + if ":" not in host: + host += ":443" + self._host = host + + # If no credentials are provided, then determine the appropriate + # defaults. + if credentials and credentials_file: + raise exceptions.DuplicateCredentialArgs( + "'credentials_file' and 'credentials' are mutually exclusive" + ) + + if credentials_file is not None: + credentials, _ = auth.load_credentials_from_file( + credentials_file, scopes=scopes, quota_project_id=quota_project_id + ) + + elif credentials is None: + credentials, _ = auth.default( + scopes=scopes, quota_project_id=quota_project_id + ) + + # Save the credentials. + self._credentials = credentials + + # Lifted into its own function so it can be stubbed out during tests. + self._prep_wrapped_messages(client_info) + + def _prep_wrapped_messages(self, client_info): + # Precompute the wrapped methods. + self._wrapped_methods = { + self.list_constraints: gapic_v1.method.wrap_method( + self.list_constraints, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=client_info, + ), + self.list_policies: gapic_v1.method.wrap_method( + self.list_policies, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=client_info, + ), + self.get_policy: gapic_v1.method.wrap_method( + self.get_policy, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=client_info, + ), + self.get_effective_policy: gapic_v1.method.wrap_method( + self.get_effective_policy, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=client_info, + ), + self.create_policy: gapic_v1.method.wrap_method( + self.create_policy, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=client_info, + ), + self.update_policy: gapic_v1.method.wrap_method( + self.update_policy, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=client_info, + ), + self.delete_policy: gapic_v1.method.wrap_method( + self.delete_policy, + default_retry=retries.Retry( + initial=1.0, + maximum=10.0, + multiplier=1.3, + predicate=retries.if_exception_type( + exceptions.DeadlineExceeded, exceptions.ServiceUnavailable, + ), + ), + default_timeout=60.0, + client_info=client_info, + ), + } + + @property + def list_constraints( + self, + ) -> typing.Callable[ + [orgpolicy.ListConstraintsRequest], + typing.Union[ + orgpolicy.ListConstraintsResponse, + typing.Awaitable[orgpolicy.ListConstraintsResponse], + ], + ]: + raise NotImplementedError() + + @property + def list_policies( + self, + ) -> typing.Callable[ + [orgpolicy.ListPoliciesRequest], + typing.Union[ + orgpolicy.ListPoliciesResponse, + typing.Awaitable[orgpolicy.ListPoliciesResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_policy( + self, + ) -> typing.Callable[ + [orgpolicy.GetPolicyRequest], + typing.Union[orgpolicy.Policy, typing.Awaitable[orgpolicy.Policy]], + ]: + raise NotImplementedError() + + @property + def get_effective_policy( + self, + ) -> typing.Callable[ + [orgpolicy.GetEffectivePolicyRequest], + typing.Union[orgpolicy.Policy, typing.Awaitable[orgpolicy.Policy]], + ]: + raise NotImplementedError() + + @property + def create_policy( + self, + ) -> typing.Callable[ + [orgpolicy.CreatePolicyRequest], + typing.Union[orgpolicy.Policy, typing.Awaitable[orgpolicy.Policy]], + ]: + raise NotImplementedError() + + @property + def update_policy( + self, + ) -> typing.Callable[ + [orgpolicy.UpdatePolicyRequest], + typing.Union[orgpolicy.Policy, typing.Awaitable[orgpolicy.Policy]], + ]: + raise NotImplementedError() + + @property + def delete_policy( + self, + ) -> typing.Callable[ + [orgpolicy.DeletePolicyRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + +__all__ = ("OrgPolicyTransport",) diff --git a/google/cloud/orgpolicy_v2/services/org_policy/transports/grpc.py b/google/cloud/orgpolicy_v2/services/org_policy/transports/grpc.py new file mode 100644 index 0000000..85a6300 --- /dev/null +++ b/google/cloud/orgpolicy_v2/services/org_policy/transports/grpc.py @@ -0,0 +1,484 @@ +# -*- 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 + +from google.api_core import grpc_helpers # type: ignore +from google.api_core import gapic_v1 # type: ignore +from google import auth # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore + +from google.cloud.orgpolicy_v2.types import orgpolicy +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import OrgPolicyTransport, DEFAULT_CLIENT_INFO + + +class OrgPolicyGrpcTransport(OrgPolicyTransport): + """gRPC backend transport for OrgPolicy. + + An interface for managing organization policies. + + The Cloud Org Policy service provides a simple mechanism for + organizations to restrict the allowed configurations across their + entire Cloud Resource hierarchy. + + You can use a ``policy`` to configure restrictions in Cloud + resources. For example, you can enforce a ``policy`` that restricts + which Google Cloud Platform APIs can be activated in a certain part + of your resource hierarchy, or prevents serial port access to VM + instances in a particular folder. + + ``Policies`` are inherited down through the resource hierarchy. A + ``policy`` applied to a parent resource automatically applies to all + its child resources unless overridden with a ``policy`` lower in the + hierarchy. + + A ``constraint`` defines an aspect of a resource's configuration + that can be controlled by an organization's policy administrator. + ``Policies`` are a collection of ``constraints`` that defines their + allowable configuration on a particular resource and its child + 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 = "orgpolicy.googleapis.com", + credentials: 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, + ) -> 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. + + 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._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + self._stubs = {} # type: Dict[str, Callable] + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + @classmethod + def create_channel( + cls, + host: str = "orgpolicy.googleapis.com", + credentials: 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: + address (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + 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. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def list_constraints( + self, + ) -> Callable[ + [orgpolicy.ListConstraintsRequest], orgpolicy.ListConstraintsResponse + ]: + r"""Return a callable for the list constraints method over gRPC. + + Lists ``Constraints`` that could be applied on the specified + resource. + + Returns: + Callable[[~.ListConstraintsRequest], + ~.ListConstraintsResponse]: + 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_constraints" not in self._stubs: + self._stubs["list_constraints"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/ListConstraints", + request_serializer=orgpolicy.ListConstraintsRequest.serialize, + response_deserializer=orgpolicy.ListConstraintsResponse.deserialize, + ) + return self._stubs["list_constraints"] + + @property + def list_policies( + self, + ) -> Callable[[orgpolicy.ListPoliciesRequest], orgpolicy.ListPoliciesResponse]: + r"""Return a callable for the list policies method over gRPC. + + Retrieves all of the ``Policies`` that exist on a particular + resource. + + Returns: + Callable[[~.ListPoliciesRequest], + ~.ListPoliciesResponse]: + 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_policies" not in self._stubs: + self._stubs["list_policies"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/ListPolicies", + request_serializer=orgpolicy.ListPoliciesRequest.serialize, + response_deserializer=orgpolicy.ListPoliciesResponse.deserialize, + ) + return self._stubs["list_policies"] + + @property + def get_policy(self) -> Callable[[orgpolicy.GetPolicyRequest], orgpolicy.Policy]: + r"""Return a callable for the get policy method over gRPC. + + Gets a ``Policy`` on a resource. + + If no ``Policy`` is set on the resource, NOT_FOUND is returned. + The ``etag`` value can be used with ``UpdatePolicy()`` to update + a ``Policy`` during read-modify-write. + + Returns: + Callable[[~.GetPolicyRequest], + ~.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_policy" not in self._stubs: + self._stubs["get_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/GetPolicy", + request_serializer=orgpolicy.GetPolicyRequest.serialize, + response_deserializer=orgpolicy.Policy.deserialize, + ) + return self._stubs["get_policy"] + + @property + def get_effective_policy( + self, + ) -> Callable[[orgpolicy.GetEffectivePolicyRequest], orgpolicy.Policy]: + r"""Return a callable for the get effective policy method over gRPC. + + Gets the effective ``Policy`` on a resource. This is the result + of merging ``Policies`` in the resource hierarchy and evaluating + conditions. The returned ``Policy`` will not have an ``etag`` or + ``condition`` set because it is a computed ``Policy`` across + multiple resources. Subtrees of Resource Manager resource + hierarchy with 'under:' prefix will not be expanded. + + Returns: + Callable[[~.GetEffectivePolicyRequest], + ~.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_effective_policy" not in self._stubs: + self._stubs["get_effective_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/GetEffectivePolicy", + request_serializer=orgpolicy.GetEffectivePolicyRequest.serialize, + response_deserializer=orgpolicy.Policy.deserialize, + ) + return self._stubs["get_effective_policy"] + + @property + def create_policy( + self, + ) -> Callable[[orgpolicy.CreatePolicyRequest], orgpolicy.Policy]: + r"""Return a callable for the create policy method over gRPC. + + Creates a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint does not exist. + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.ALREADY_EXISTS`` if the policy already exists + on the given Cloud resource. + + Returns: + Callable[[~.CreatePolicyRequest], + ~.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 "create_policy" not in self._stubs: + self._stubs["create_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/CreatePolicy", + request_serializer=orgpolicy.CreatePolicyRequest.serialize, + response_deserializer=orgpolicy.Policy.deserialize, + ) + return self._stubs["create_policy"] + + @property + def update_policy( + self, + ) -> Callable[[orgpolicy.UpdatePolicyRequest], orgpolicy.Policy]: + r"""Return a callable for the update policy method over gRPC. + + Updates a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint or the policy do + not exist. Returns a ``google.rpc.Status`` with + ``google.rpc.Code.ABORTED`` if the etag supplied in the request + does not match the persisted etag of the policy + + Note: the supplied policy will perform a full overwrite of all + fields. + + Returns: + Callable[[~.UpdatePolicyRequest], + ~.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 "update_policy" not in self._stubs: + self._stubs["update_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/UpdatePolicy", + request_serializer=orgpolicy.UpdatePolicyRequest.serialize, + response_deserializer=orgpolicy.Policy.deserialize, + ) + return self._stubs["update_policy"] + + @property + def delete_policy(self) -> Callable[[orgpolicy.DeletePolicyRequest], empty.Empty]: + r"""Return a callable for the delete policy method over gRPC. + + Deletes a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint or Org Policy + does not exist. + + Returns: + Callable[[~.DeletePolicyRequest], + ~.Empty]: + 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_policy" not in self._stubs: + self._stubs["delete_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/DeletePolicy", + request_serializer=orgpolicy.DeletePolicyRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_policy"] + + +__all__ = ("OrgPolicyGrpcTransport",) diff --git a/google/cloud/orgpolicy_v2/services/org_policy/transports/grpc_asyncio.py b/google/cloud/orgpolicy_v2/services/org_policy/transports/grpc_asyncio.py new file mode 100644 index 0000000..33d57ed --- /dev/null +++ b/google/cloud/orgpolicy_v2/services/org_policy/transports/grpc_asyncio.py @@ -0,0 +1,494 @@ +# -*- 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 + +from google.api_core import gapic_v1 # type: ignore +from google.api_core import grpc_helpers_async # type: ignore +from google import auth # type: ignore +from google.auth import credentials # type: ignore +from google.auth.transport.grpc import SslCredentials # type: ignore + +import grpc # type: ignore +from grpc.experimental import aio # type: ignore + +from google.cloud.orgpolicy_v2.types import orgpolicy +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import OrgPolicyTransport, DEFAULT_CLIENT_INFO +from .grpc import OrgPolicyGrpcTransport + + +class OrgPolicyGrpcAsyncIOTransport(OrgPolicyTransport): + """gRPC AsyncIO backend transport for OrgPolicy. + + An interface for managing organization policies. + + The Cloud Org Policy service provides a simple mechanism for + organizations to restrict the allowed configurations across their + entire Cloud Resource hierarchy. + + You can use a ``policy`` to configure restrictions in Cloud + resources. For example, you can enforce a ``policy`` that restricts + which Google Cloud Platform APIs can be activated in a certain part + of your resource hierarchy, or prevents serial port access to VM + instances in a particular folder. + + ``Policies`` are inherited down through the resource hierarchy. A + ``policy`` applied to a parent resource automatically applies to all + its child resources unless overridden with a ``policy`` lower in the + hierarchy. + + A ``constraint`` defines an aspect of a resource's configuration + that can be controlled by an organization's policy administrator. + ``Policies`` are a collection of ``constraints`` that defines their + allowable configuration on a particular resource and its child + 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 = "orgpolicy.googleapis.com", + credentials: 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: + address (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + 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. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers_async.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + def __init__( + self, + *, + host: str = "orgpolicy.googleapis.com", + credentials: 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, + ) -> 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. + + 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._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + self._stubs = {} + + @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 list_constraints( + self, + ) -> Callable[ + [orgpolicy.ListConstraintsRequest], Awaitable[orgpolicy.ListConstraintsResponse] + ]: + r"""Return a callable for the list constraints method over gRPC. + + Lists ``Constraints`` that could be applied on the specified + resource. + + Returns: + Callable[[~.ListConstraintsRequest], + Awaitable[~.ListConstraintsResponse]]: + 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_constraints" not in self._stubs: + self._stubs["list_constraints"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/ListConstraints", + request_serializer=orgpolicy.ListConstraintsRequest.serialize, + response_deserializer=orgpolicy.ListConstraintsResponse.deserialize, + ) + return self._stubs["list_constraints"] + + @property + def list_policies( + self, + ) -> Callable[ + [orgpolicy.ListPoliciesRequest], Awaitable[orgpolicy.ListPoliciesResponse] + ]: + r"""Return a callable for the list policies method over gRPC. + + Retrieves all of the ``Policies`` that exist on a particular + resource. + + Returns: + Callable[[~.ListPoliciesRequest], + Awaitable[~.ListPoliciesResponse]]: + 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_policies" not in self._stubs: + self._stubs["list_policies"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/ListPolicies", + request_serializer=orgpolicy.ListPoliciesRequest.serialize, + response_deserializer=orgpolicy.ListPoliciesResponse.deserialize, + ) + return self._stubs["list_policies"] + + @property + def get_policy( + self, + ) -> Callable[[orgpolicy.GetPolicyRequest], Awaitable[orgpolicy.Policy]]: + r"""Return a callable for the get policy method over gRPC. + + Gets a ``Policy`` on a resource. + + If no ``Policy`` is set on the resource, NOT_FOUND is returned. + The ``etag`` value can be used with ``UpdatePolicy()`` to update + a ``Policy`` during read-modify-write. + + Returns: + Callable[[~.GetPolicyRequest], + 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_policy" not in self._stubs: + self._stubs["get_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/GetPolicy", + request_serializer=orgpolicy.GetPolicyRequest.serialize, + response_deserializer=orgpolicy.Policy.deserialize, + ) + return self._stubs["get_policy"] + + @property + def get_effective_policy( + self, + ) -> Callable[[orgpolicy.GetEffectivePolicyRequest], Awaitable[orgpolicy.Policy]]: + r"""Return a callable for the get effective policy method over gRPC. + + Gets the effective ``Policy`` on a resource. This is the result + of merging ``Policies`` in the resource hierarchy and evaluating + conditions. The returned ``Policy`` will not have an ``etag`` or + ``condition`` set because it is a computed ``Policy`` across + multiple resources. Subtrees of Resource Manager resource + hierarchy with 'under:' prefix will not be expanded. + + Returns: + Callable[[~.GetEffectivePolicyRequest], + 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_effective_policy" not in self._stubs: + self._stubs["get_effective_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/GetEffectivePolicy", + request_serializer=orgpolicy.GetEffectivePolicyRequest.serialize, + response_deserializer=orgpolicy.Policy.deserialize, + ) + return self._stubs["get_effective_policy"] + + @property + def create_policy( + self, + ) -> Callable[[orgpolicy.CreatePolicyRequest], Awaitable[orgpolicy.Policy]]: + r"""Return a callable for the create policy method over gRPC. + + Creates a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint does not exist. + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.ALREADY_EXISTS`` if the policy already exists + on the given Cloud resource. + + Returns: + Callable[[~.CreatePolicyRequest], + 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 "create_policy" not in self._stubs: + self._stubs["create_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/CreatePolicy", + request_serializer=orgpolicy.CreatePolicyRequest.serialize, + response_deserializer=orgpolicy.Policy.deserialize, + ) + return self._stubs["create_policy"] + + @property + def update_policy( + self, + ) -> Callable[[orgpolicy.UpdatePolicyRequest], Awaitable[orgpolicy.Policy]]: + r"""Return a callable for the update policy method over gRPC. + + Updates a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint or the policy do + not exist. Returns a ``google.rpc.Status`` with + ``google.rpc.Code.ABORTED`` if the etag supplied in the request + does not match the persisted etag of the policy + + Note: the supplied policy will perform a full overwrite of all + fields. + + Returns: + Callable[[~.UpdatePolicyRequest], + 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 "update_policy" not in self._stubs: + self._stubs["update_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/UpdatePolicy", + request_serializer=orgpolicy.UpdatePolicyRequest.serialize, + response_deserializer=orgpolicy.Policy.deserialize, + ) + return self._stubs["update_policy"] + + @property + def delete_policy( + self, + ) -> Callable[[orgpolicy.DeletePolicyRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete policy method over gRPC. + + Deletes a Policy. + + Returns a ``google.rpc.Status`` with + ``google.rpc.Code.NOT_FOUND`` if the constraint or Org Policy + does not exist. + + Returns: + Callable[[~.DeletePolicyRequest], + Awaitable[~.Empty]]: + 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_policy" not in self._stubs: + self._stubs["delete_policy"] = self.grpc_channel.unary_unary( + "/google.cloud.orgpolicy.v2.OrgPolicy/DeletePolicy", + request_serializer=orgpolicy.DeletePolicyRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_policy"] + + +__all__ = ("OrgPolicyGrpcAsyncIOTransport",) diff --git a/google/cloud/orgpolicy_v2/types/__init__.py b/google/cloud/orgpolicy_v2/types/__init__.py new file mode 100644 index 0000000..c2c6ae8 --- /dev/null +++ b/google/cloud/orgpolicy_v2/types/__init__.py @@ -0,0 +1,48 @@ +# -*- 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 .constraint import Constraint +from .orgpolicy import ( + Policy, + AlternatePolicySpec, + PolicySpec, + ListConstraintsRequest, + ListConstraintsResponse, + ListPoliciesRequest, + ListPoliciesResponse, + GetPolicyRequest, + GetEffectivePolicyRequest, + CreatePolicyRequest, + UpdatePolicyRequest, + DeletePolicyRequest, +) + +__all__ = ( + "Constraint", + "Policy", + "AlternatePolicySpec", + "PolicySpec", + "ListConstraintsRequest", + "ListConstraintsResponse", + "ListPoliciesRequest", + "ListPoliciesResponse", + "GetPolicyRequest", + "GetEffectivePolicyRequest", + "CreatePolicyRequest", + "UpdatePolicyRequest", + "DeletePolicyRequest", +) diff --git a/google/cloud/orgpolicy_v2/types/constraint.py b/google/cloud/orgpolicy_v2/types/constraint.py new file mode 100644 index 0000000..1414ec0 --- /dev/null +++ b/google/cloud/orgpolicy_v2/types/constraint.py @@ -0,0 +1,135 @@ +# -*- 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.orgpolicy.v2", manifest={"Constraint",}, +) + + +class Constraint(proto.Message): + r"""A ``constraint`` describes a way to restrict resource's + configuration. For example, you could enforce a constraint that + controls which cloud services can be activated across an + organization, or whether a Compute Engine instance can have serial + port connections established. ``Constraints`` can be configured by + the organization's policy adminstrator to fit the needs of the + organzation by setting a ``policy`` that includes ``constraints`` at + different locations in the organization's resource hierarchy. + Policies are inherited down the resource hierarchy from higher + levels, but can also be overridden. For details about the + inheritance rules please read about + [``policies``][google.cloud.OrgPolicy.v2.Policy]. + + ``Constraints`` have a default behavior determined by the + ``constraint_default`` field, which is the enforcement behavior that + is used in the absence of a ``policy`` being defined or inherited + for the resource in question. + + Attributes: + name (str): + Immutable. The resource name of the Constraint. Must be in + one of the following forms: + + - ``projects/{project_number}/constraints/{constraint_name}`` + - ``folders/{folder_id}/constraints/{constraint_name}`` + - ``organizations/{organization_id}/constraints/{constraint_name}`` + + For example, + "/projects/123/constraints/compute.disableSerialPortAccess". + display_name (str): + The human readable name. + Mutable. + description (str): + Detailed description of what this ``Constraint`` controls as + well as how and where it is enforced. + + Mutable. + constraint_default (google.cloud.orgpolicy_v2.types.Constraint.ConstraintDefault): + The evaluation behavior of this constraint in + the absence of 'Policy'. + list_constraint (google.cloud.orgpolicy_v2.types.Constraint.ListConstraint): + Defines this constraint as being a + ListConstraint. + boolean_constraint (google.cloud.orgpolicy_v2.types.Constraint.BooleanConstraint): + Defines this constraint as being a + BooleanConstraint. + """ + + class ConstraintDefault(proto.Enum): + r"""Specifies the default behavior in the absence of any ``Policy`` for + the ``Constraint``. This must not be + ``CONSTRAINT_DEFAULT_UNSPECIFIED``. + + Immutable after creation. + """ + CONSTRAINT_DEFAULT_UNSPECIFIED = 0 + ALLOW = 1 + DENY = 2 + + class ListConstraint(proto.Message): + r"""A ``Constraint`` that allows or disallows a list of string values, + which are configured by an Organization's policy administrator with + a ``Policy``. + + Attributes: + supports_in (bool): + Indicates whether values grouped into categories can be used + in ``Policy.allowed_values`` and ``Policy.denied_values``. + For example, ``"in:Python"`` would match any value in the + 'Python' group. + supports_under (bool): + Indicates whether subtrees of Cloud Resource Manager + resource hierarchy can be used in ``Policy.allowed_values`` + and ``Policy.denied_values``. For example, + ``"under:folders/123"`` would match any resource under the + 'folders/123' folder. + """ + + supports_in = proto.Field(proto.BOOL, number=1) + + supports_under = proto.Field(proto.BOOL, number=2) + + class BooleanConstraint(proto.Message): + r"""A ``Constraint`` that is either enforced or not. + + For example a constraint + ``constraints/compute.disableSerialPortAccess``. If it is enforced + on a VM instance, serial port connections will not be opened to that + instance. + """ + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + description = proto.Field(proto.STRING, number=3) + + constraint_default = proto.Field(proto.ENUM, number=4, enum=ConstraintDefault,) + + list_constraint = proto.Field( + proto.MESSAGE, number=5, oneof="constraint_type", message=ListConstraint, + ) + + boolean_constraint = proto.Field( + proto.MESSAGE, number=6, oneof="constraint_type", message=BooleanConstraint, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/orgpolicy_v2/types/orgpolicy.py b/google/cloud/orgpolicy_v2/types/orgpolicy.py new file mode 100644 index 0000000..ad5db56 --- /dev/null +++ b/google/cloud/orgpolicy_v2/types/orgpolicy.py @@ -0,0 +1,427 @@ +# -*- 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.cloud.orgpolicy_v2.types import constraint +from google.protobuf import timestamp_pb2 as timestamp # type: ignore +from google.type import expr_pb2 as expr # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.orgpolicy.v2", + manifest={ + "Policy", + "AlternatePolicySpec", + "PolicySpec", + "ListConstraintsRequest", + "ListConstraintsResponse", + "ListPoliciesRequest", + "ListPoliciesResponse", + "GetPolicyRequest", + "GetEffectivePolicyRequest", + "CreatePolicyRequest", + "UpdatePolicyRequest", + "DeletePolicyRequest", + }, +) + + +class Policy(proto.Message): + r"""Defines a Cloud Organization ``Policy`` which is used to specify + ``Constraints`` for configurations of Cloud Platform resources. + + Attributes: + name (str): + Immutable. The resource name of the Policy. Must be one of + the following forms, where constraint_name is the name of + the constraint which this Policy configures: + + - ``projects/{project_number}/policies/{constraint_name}`` + - ``folders/{folder_id}/policies/{constraint_name}`` + - ``organizations/{organization_id}/policies/{constraint_name}`` + + For example, + "projects/123/policies/compute.disableSerialPortAccess". + + Note: ``projects/{project_id}/policies/{constraint_name}`` + is also an acceptable name for API requests, but responses + will return the name using the equivalent project number. + spec (google.cloud.orgpolicy_v2.types.PolicySpec): + Basic information about the Organization + Policy. + alternate (google.cloud.orgpolicy_v2.types.AlternatePolicySpec): + An alternate policy configuration that will + be used instead of the baseline policy + configurations as determined by the launch. + Currently the only way the launch can trigger + the alternate configuration is via dry- + run/darklaunch. + """ + + name = proto.Field(proto.STRING, number=1) + + spec = proto.Field(proto.MESSAGE, number=2, message="PolicySpec",) + + alternate = proto.Field(proto.MESSAGE, number=3, message="AlternatePolicySpec",) + + +class AlternatePolicySpec(proto.Message): + r"""Similar to PolicySpec but with an extra 'launch' field for + launch reference. The PolicySpec here is specific for dry- + run/darklaunch. + + Attributes: + launch (str): + Reference to the launch that will be used + while audit logging and to control the launch. + Should be set only in the alternate policy. + spec (google.cloud.orgpolicy_v2.types.PolicySpec): + Specify ``Constraint`` for configurations of Cloud Platform + resources. + """ + + launch = proto.Field(proto.STRING, number=1) + + spec = proto.Field(proto.MESSAGE, number=2, message="PolicySpec",) + + +class PolicySpec(proto.Message): + r"""Defines a Cloud Organization ``PolicySpec`` which is used to specify + ``Constraints`` for configurations of Cloud Platform resources. + + Attributes: + etag (str): + An opaque tag indicating the current version of the + ``Policy``, used for concurrency control. + + This field is ignored if used in a ``CreatePolicy`` request. + + When the ``Policy`` is returned from either a ``GetPolicy`` + or a ``ListPolicies`` request, this ``etag`` indicates the + version of the current ``Policy`` to use when executing a + read-modify-write loop. + + When the ``Policy`` is returned from a + ``GetEffectivePolicy`` request, the ``etag`` will be unset. + update_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The time stamp this was previously updated. + This represents the last time a call to ``CreatePolicy`` or + ``UpdatePolicy`` was made for that ``Policy``. + rules (Sequence[google.cloud.orgpolicy_v2.types.PolicySpec.PolicyRule]): + Up to 10 PolicyRules are allowed. + + In Policies for boolean constraints, the following + requirements apply: + + - There must be one and only one PolicyRule where condition + is unset. + - BooleanPolicyRules with conditions must set ``enforced`` + to the opposite of the PolicyRule without a condition. + - During policy evaluation, PolicyRules with conditions + that are true for a target resource take precedence. + inherit_from_parent (bool): + Determines the inheritance behavior for this ``Policy``. + + If ``inherit_from_parent`` is true, PolicyRules set higher + up in the hierarchy (up to the closest root) are inherited + and present in the effective policy. If it is false, then no + rules are inherited, and this Policy becomes the new root + for evaluation. This field can be set only for Policies + which configure list constraints. + reset (bool): + Ignores policies set above this resource and restores the + ``constraint_default`` enforcement behavior of the specific + ``Constraint`` at this resource. This field can be set in + policies for either list or boolean constraints. If set, + ``rules`` must be empty and ``inherit_from_parent`` must be + set to false. + """ + + class PolicyRule(proto.Message): + r"""A rule used to express this policy. + + Attributes: + values (google.cloud.orgpolicy_v2.types.PolicySpec.PolicyRule.StringValues): + List of values to be used for this + PolicyRule. This field can be set only in + Policies for list constraints. + allow_all (bool): + Setting this to true means that all values + are allowed. This field can be set only in + Policies for list constraints. + deny_all (bool): + Setting this to true means that all values + are denied. This field can be set only in + Policies for list constraints. + enforce (bool): + If ``true``, then the ``Policy`` is enforced. If ``false``, + then any configuration is acceptable. This field can be set + only in Policies for boolean constraints. + condition (google.type.expr_pb2.Expr): + A condition which determines whether this rule is used in + the evaluation of the policy. When set, the ``expression`` + field in the \`Expr' must include from 1 to 10 + subexpressions, joined by the "||" or "&&" operators. Each + subexpression must be of the form + "resource.matchLabels(key_name, value_name)", where key_name + and value_name are the resource names for Label Keys and + Values. These names are available from the Label Manager + Service. An example expression is: + "resource.matchLabels('labelKeys/123, 'labelValues/456')". + """ + + class StringValues(proto.Message): + r"""A message that holds specific allowed and denied values. This + message can define specific values and subtrees of Cloud Resource + Manager resource hierarchy (``Organizations``, ``Folders``, + ``Projects``) that are allowed or denied. This is achieved by using + the ``under:`` and optional ``is:`` prefixes. The ``under:`` prefix + is used to denote resource subtree values. The ``is:`` prefix is + used to denote specific values, and is required only if the value + contains a ":". Values prefixed with "is:" are treated the same as + values with no prefix. Ancestry subtrees must be in one of the + following formats: - "projects/", e.g. "projects/tokyo-rain-123" - + "folders/", e.g. "folders/1234" - "organizations/", e.g. + "organizations/1234" The ``supports_under`` field of the associated + ``Constraint`` defines whether ancestry prefixes can be used. + + Attributes: + allowed_values (Sequence[str]): + List of values allowed at this resource. + denied_values (Sequence[str]): + List of values denied at this resource. + """ + + allowed_values = proto.RepeatedField(proto.STRING, number=1) + + denied_values = proto.RepeatedField(proto.STRING, number=2) + + values = proto.Field( + proto.MESSAGE, + number=1, + oneof="kind", + message="PolicySpec.PolicyRule.StringValues", + ) + + allow_all = proto.Field(proto.BOOL, number=2, oneof="kind") + + deny_all = proto.Field(proto.BOOL, number=3, oneof="kind") + + enforce = proto.Field(proto.BOOL, number=4, oneof="kind") + + condition = proto.Field(proto.MESSAGE, number=5, message=expr.Expr,) + + etag = proto.Field(proto.STRING, number=1) + + update_time = proto.Field(proto.MESSAGE, number=2, message=timestamp.Timestamp,) + + rules = proto.RepeatedField(proto.MESSAGE, number=3, message=PolicyRule,) + + inherit_from_parent = proto.Field(proto.BOOL, number=4) + + reset = proto.Field(proto.BOOL, number=5) + + +class ListConstraintsRequest(proto.Message): + r"""The request sent to the [ListConstraints] + [google.cloud.orgpolicy.v2.OrgPolicy.ListConstraints] method. + + Attributes: + parent (str): + Required. The Cloud resource that parents the constraint. + Must be in one of the following forms: + + - ``projects/{project_number}`` + - ``projects/{project_id}`` + - ``folders/{folder_id}`` + - ``organizations/{organization_id}`` + page_size (int): + Size of the pages to be returned. This is + currently unsupported and will be ignored. The + server may at any point start using this field + to limit page size. + page_token (str): + Page token used to retrieve the next page. + This is currently unsupported and will be + ignored. The server may at any point start using + this field. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class ListConstraintsResponse(proto.Message): + r"""The response returned from the [ListConstraints] + [google.cloud.orgpolicy.v2.OrgPolicy.ListConstraints] method. + + Attributes: + constraints (Sequence[google.cloud.orgpolicy_v2.types.Constraint]): + The collection of constraints that are + available on the targeted resource. + next_page_token (str): + Page token used to retrieve the next page. + This is currently not used. + """ + + @property + def raw_page(self): + return self + + constraints = proto.RepeatedField( + proto.MESSAGE, number=1, message=constraint.Constraint, + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class ListPoliciesRequest(proto.Message): + r"""The request sent to the [ListPolicies] + [google.cloud.orgpolicy.v2.OrgPolicy.ListPolicies] method. + + Attributes: + parent (str): + Required. The target Cloud resource that parents the set of + constraints and policies that will be returned from this + call. Must be in one of the following forms: + + - ``projects/{project_number}`` + - ``projects/{project_id}`` + - ``folders/{folder_id}`` + - ``organizations/{organization_id}`` + page_size (int): + Size of the pages to be returned. This is + currently unsupported and will be ignored. The + server may at any point start using this field + to limit page size. + page_token (str): + Page token used to retrieve the next page. + This is currently unsupported and will be + ignored. The server may at any point start using + this field. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class ListPoliciesResponse(proto.Message): + r"""The response returned from the [ListPolicies] + [google.cloud.orgpolicy.v2.OrgPolicy.ListPolicies] method. It will + be empty if no ``Policies`` are set on the resource. + + Attributes: + policies (Sequence[google.cloud.orgpolicy_v2.types.Policy]): + All ``Policies`` that exist on the resource. It will be + empty if no ``Policies`` are set. + next_page_token (str): + Page token used to retrieve the next page. + This is currently not used, but the server may + at any point start supplying a valid token. + """ + + @property + def raw_page(self): + return self + + policies = proto.RepeatedField(proto.MESSAGE, number=1, message="Policy",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetPolicyRequest(proto.Message): + r"""The request sent to the [GetPolicy] + [google.cloud.orgpolicy.v2.OrgPolicy.GetPolicy] method. + + Attributes: + name (str): + Required. Resource name of the policy. See ``Policy`` for + naming requirements. + """ + + name = proto.Field(proto.STRING, number=1) + + +class GetEffectivePolicyRequest(proto.Message): + r"""The request sent to the [GetEffectivePolicy] + [google.cloud.orgpolicy.v2.OrgPolicy.GetEffectivePolicy] method. + + Attributes: + name (str): + Required. The effective policy to compute. See ``Policy`` + for naming rules. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreatePolicyRequest(proto.Message): + r"""The request sent to the [CreatePolicyRequest] + [google.cloud.orgpolicy.v2.OrgPolicy.CreatePolicy] method. + + Attributes: + parent (str): + Required. The Cloud resource that will parent the new + Policy. Must be in one of the following forms: + + - ``projects/{project_number}`` + - ``projects/{project_id}`` + - ``folders/{folder_id}`` + - ``organizations/{organization_id}`` + policy (google.cloud.orgpolicy_v2.types.Policy): + Required. ``Policy`` to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + policy = proto.Field(proto.MESSAGE, number=3, message="Policy",) + + +class UpdatePolicyRequest(proto.Message): + r"""The request sent to the [UpdatePolicyRequest] + [google.cloud.orgpolicy.v2.OrgPolicy.UpdatePolicy] method. + + Attributes: + policy (google.cloud.orgpolicy_v2.types.Policy): + Required. ``Policy`` to update. + """ + + policy = proto.Field(proto.MESSAGE, number=1, message="Policy",) + + +class DeletePolicyRequest(proto.Message): + r"""The request sent to the [DeletePolicy] + [google.cloud.orgpolicy.v2.OrgPolicy.DeletePolicy] method. + + Attributes: + name (str): + Required. Name of the policy to delete. See ``Policy`` for + naming rules. + """ + + name = proto.Field(proto.STRING, number=1) + + +__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 6ad696b..51379d4 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,4 +1,6 @@ -# Copyright 2020 Google LLC +# -*- coding: utf-8 -*- +# +# Copyright 2018 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,100 +14,118 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Generated by synthtool. DO NOT EDIT! + +from __future__ import absolute_import import os import shutil -from pathlib import Path import nox -BLACK_VERSION = "black==19.3b0" + +BLACK_VERSION = "black==19.10b0" +BLACK_PATHS = ["docs", "google", "tests", "noxfile.py", "setup.py"] + +DEFAULT_PYTHON_VERSION = "3.8" +SYSTEM_TEST_PYTHON_VERSIONS = ["3.8"] +UNIT_TEST_PYTHON_VERSIONS = ["3.6", "3.7", "3.8", "3.9"] + +# 'docfx' is excluded since it only needs to run in 'docs-presubmit' +nox.options.sessions = [ + "unit", + "system", + "cover", + "lint", + "lint_setup_py", + "blacken", + "docs", +] + + +@nox.session(python="3.8") +def generate_protos(session): + """Generates the protos using protoc. + + Some notes on the `google` directory: + 1. The `_pb2.py` files are produced by protoc. + 2. The .proto files are non-functional but are left in the repository + to make it easier to understand diffs. + 3. The `google` directory also has `__init__.py` files to create proper modules. + If a new subdirectory is added, you will need to create more `__init__.py` + files. + + NOTE: This is a hack and only runnable locally. You will need to have + the api-common-protos repo cloned. This should be migrated to use + bazel in the future. + """ + session.install("grpcio-tools") + protos = [str(p) for p in (Path(".").glob("google/**/*.proto"))] + + session.run( + "python", + "-m", + "grpc_tools.protoc", + "--proto_path=../api-common-protos", + "--proto_path=.", + "--python_out=.", + *protos, + ) + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def lint(session): + """Run linters. + + Returns a failure if the linters find linting errors or sufficiently + serious code quality issues. + """ + session.install("flake8", BLACK_VERSION) + session.run( + "black", "--check", *BLACK_PATHS, + ) + session.run("flake8", "google", "tests") @nox.session(python="3.6") 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. """ session.install(BLACK_VERSION) - session.run("black", "google", "setup.py") + session.run( + "black", *BLACK_PATHS, + ) -@nox.session(python="3.8") +@nox.session(python=DEFAULT_PYTHON_VERSION) def lint_setup_py(session): - """Verify that setup.py is valid""" + """Verify that setup.py is valid (including RST check).""" session.install("docutils", "pygments") - session.run("python", "setup.py", "check", "--strict") + session.run("python", "setup.py", "check", "--restructuredtext", "--strict") -# @nox.session(python="3.8") -# def generate_protos(session): -# """Generates the protos using protoc. - -# Some notes on the `google` directory: -# 1. The `_pb2.py` files are produced by protoc. -# 2. The .proto files are non-functional but are left in the repository -# to make it easier to understand diffs. -# 3. The `google` directory also has `__init__.py` files to create proper modules. -# If a new subdirectory is added, you will need to create more `__init__.py` -# files. - -# NOTE: This is a hack and only runnable locally. You will need to have -# the api-common-protos repo cloned. This should be migrated to use -# bazel in the future. -# """ -# session.install("grpcio-tools") -# protos = [str(p) for p in (Path(".").glob("google/**/*.proto"))] - -# session.run( -# "python", -# "-m", -# "grpc_tools.protoc", -# "--proto_path=../api-common-protos", -# "--proto_path=.", -# "--python_out=.", -# *protos, -# ) - -@nox.session(python="3.7") -def docs(session): - """Build the docs for this library.""" - session.install("-e", ".") - session.install("sphinx<3.0.0", "alabaster", "recommonmark") +def default(session): + # Install all test dependencies, then install this package in-place. + session.install("asyncmock", "pytest-asyncio") - shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) - session.run( - "sphinx-build", - "-W", # warnings as errors - "-T", # show full traceback on exception - "-N", # no colors - "-b", - "html", - "-d", - os.path.join("docs", "_build", "doctrees", ""), - os.path.join("docs", ""), - os.path.join("docs", "_build", "html", ""), + session.install( + "mock", "pytest", "pytest-cov", ) - -def default(session): - # Install all test dependencies, then install this package in-place. - session.install("mock", "pytest", "pytest-cov") session.install("-e", ".") - # Install this library - # This *must* be the last install command to get the package from source. - session.install("..") - # Run py.test against the unit tests. session.run( "py.test", "--quiet", - "--cov=google.cloud.cloudasset", - "--cov=google.cloud", - "--cov=tests.unit", + f"--junitxml=unit_{session.python}_sponge_log.xml", + "--cov=google/cloud", + "--cov=tests/unit", "--cov-append", "--cov-config=.coveragerc", "--cov-report=", @@ -115,17 +135,23 @@ def default(session): ) +@nox.session(python=UNIT_TEST_PYTHON_VERSIONS) def unit(session): """Run the unit test suite.""" default(session) +@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS) def system(session): """Run the system test suite.""" 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("FIRESTORE_APPLICATION_CREDENTIALS", ""): + if not os.environ.get("GOOGLE_APPLICATION_CREDENTIALS", ""): session.skip("Credentials must be set via environment variable") system_test_exists = os.path.exists(system_test_path) @@ -139,47 +165,95 @@ def system(session): # Install all test dependencies, then install this package into the # virtualenv's dist-packages. - session.install("mock", "pytest") - + session.install( + "mock", "pytest", "google-cloud-testutils", + ) session.install("-e", ".") - # Install this library - # This *must* be the last install command to get the package from source. - session.install("..") - # Run py.test against the system tests. if system_test_exists: - session.run("py.test", "--verbose", 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", "--verbose", system_test_folder_path, *session.posargs) - - -@nox.parametrize( - "library", - ["python-asset"], - ids=["asset"], -) -@nox.session(python=["2.7", "3.5", "3.6", "3.7", "3.8"]) -def test(session, library): - """Run tests from a downstream libraries. - To verify that any changes we make here will not break downstream libraries, clone - a few and run their unit and system tests. - NOTE: The unit and system test functions above are copied from the templates. - They will need to be updated when the templates change. - """ - try: - session.run("git", "-C", library, "pull", external=True) - except nox.command.CommandFailed: session.run( - "git", - "clone", - "--single-branch", - f"https://github.com/googleapis/{library}", - external=True, + "py.test", + "--quiet", + f"--junitxml=system_{session.python}_sponge_log.xml", + system_test_folder_path, + *session.posargs, ) - session.cd(library) - unit(session) - # system tests are run on 2.7 and 3.7 only - if session.python == "2.7" or session.python == "3.7": - system(session) + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def cover(session): + """Run the final coverage report. + + This outputs the coverage report aggregating coverage from the unit + test runs (not system test runs), and then erases coverage data. + """ + session.install("coverage", "pytest-cov") + session.run("coverage", "report", "--show-missing", "--fail-under=97") + + session.run("coverage", "erase") + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +def docs(session): + """Build the docs for this library.""" + + session.install("-e", ".") + session.install("sphinx", "alabaster", "recommonmark") + + shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) + session.run( + "sphinx-build", + "-W", # warnings as errors + "-T", # show full traceback on exception + "-N", # no colors + "-b", + "html", + "-d", + os.path.join("docs", "_build", "doctrees", ""), + os.path.join("docs", ""), + os.path.join("docs", "_build", "html", ""), + ) + + +@nox.session(python=DEFAULT_PYTHON_VERSION) +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") + + shutil.rmtree(os.path.join("docs", "_build"), ignore_errors=True) + session.run( + "sphinx-build", + "-T", # show full traceback on exception + "-N", # no colors + "-D", + ( + "extensions=sphinx.ext.autodoc," + "sphinx.ext.autosummary," + "docfx_yaml.extension," + "sphinx.ext.intersphinx," + "sphinx.ext.coverage," + "sphinx.ext.napoleon," + "sphinx.ext.todo," + "sphinx.ext.viewcode," + "recommonmark" + ), + "-b", + "html", + "-d", + os.path.join("docs", "_build", "doctrees", ""), + os.path.join("docs", ""), + os.path.join("docs", "_build", "html", ""), + ) diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh new file mode 100755 index 0000000..21f6d2a --- /dev/null +++ b/scripts/decrypt-secrets.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ROOT=$( dirname "$DIR" ) + +# Work from the project root. +cd $ROOT + +# Prevent it from overriding files. +# We recommend that sample authors use their own service account files and cloud project. +# In that case, they are supposed to prepare these files by themselves. +if [[ -f "testing/test-env.sh" ]] || \ + [[ -f "testing/service-account.json" ]] || \ + [[ -f "testing/client-secrets.json" ]]; then + echo "One or more target files exist, aborting." + exit 1 +fi + +# Use SECRET_MANAGER_PROJECT if set, fallback to cloud-devrel-kokoro-resources. +PROJECT_ID="${SECRET_MANAGER_PROJECT:-cloud-devrel-kokoro-resources}" + +gcloud secrets versions access latest --secret="python-docs-samples-test-env" \ + --project="${PROJECT_ID}" \ + > testing/test-env.sh +gcloud secrets versions access latest \ + --secret="python-docs-samples-service-account" \ + --project="${PROJECT_ID}" \ + > testing/service-account.json +gcloud secrets versions access latest \ + --secret="python-docs-samples-client-secrets" \ + --project="${PROJECT_ID}" \ + > testing/client-secrets.json diff --git a/scripts/fixup_orgpolicy_v2_keywords.py b/scripts/fixup_orgpolicy_v2_keywords.py new file mode 100644 index 0000000..e85a43c --- /dev/null +++ b/scripts/fixup_orgpolicy_v2_keywords.py @@ -0,0 +1,185 @@ +#! /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 orgpolicyCallTransformer(cst.CSTTransformer): + CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') + METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { + 'create_policy': ('parent', 'policy', ), + 'delete_policy': ('name', ), + 'get_effective_policy': ('name', ), + 'get_policy': ('name', ), + 'list_constraints': ('parent', 'page_size', 'page_token', ), + 'list_policies': ('parent', 'page_size', 'page_token', ), + 'update_policy': ('policy', ), + + } + + 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=orgpolicyCallTransformer(), +): + """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 orgpolicy client library. + +The existing sources are NOT overwritten but are copied to output_dir with changes made. + +Note: This tool operates at a best-effort level at converting positional + parameters in client method calls to keyword based parameters. + Cases where it WILL FAIL include + A) * or ** expansion in a method call. + B) Calls via function or method alias (includes free function calls) + C) Indirect or dispatched calls (e.g. the method is looked up dynamically) + + These all constitute false negatives. The tool will also detect false + positives when an API method shares a name with another method. +""") + parser.add_argument( + '-d', + '--input-directory', + required=True, + dest='input_dir', + help='the input directory to walk for python files to fix up', + ) + parser.add_argument( + '-o', + '--output-directory', + required=True, + dest='output_dir', + help='the directory to output files fixed via un-flattening', + ) + args = parser.parse_args() + input_dir = pathlib.Path(args.input_dir) + output_dir = pathlib.Path(args.output_dir) + if not input_dir.is_dir(): + print( + f"input directory '{input_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if not output_dir.is_dir(): + print( + f"output directory '{output_dir}' does not exist or is not a directory", + file=sys.stderr, + ) + sys.exit(-1) + + if os.listdir(output_dir): + print( + f"output directory '{output_dir}' is not empty", + file=sys.stderr, + ) + sys.exit(-1) + + fix_files(input_dir, output_dir) diff --git a/scripts/readme-gen/readme_gen.py b/scripts/readme-gen/readme_gen.py new file mode 100644 index 0000000..d309d6e --- /dev/null +++ b/scripts/readme-gen/readme_gen.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +# Copyright 2016 Google Inc +# +# 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. + +"""Generates READMEs using configuration defined in yaml.""" + +import argparse +import io +import os +import subprocess + +import jinja2 +import yaml + + +jinja_env = jinja2.Environment( + trim_blocks=True, + loader=jinja2.FileSystemLoader( + os.path.abspath(os.path.join(os.path.dirname(__file__), 'templates')))) + +README_TMPL = jinja_env.get_template('README.tmpl.rst') + + +def get_help(file): + return subprocess.check_output(['python', file, '--help']).decode() + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('source') + parser.add_argument('--destination', default='README.rst') + + args = parser.parse_args() + + source = os.path.abspath(args.source) + root = os.path.dirname(source) + destination = os.path.join(root, args.destination) + + jinja_env.globals['get_help'] = get_help + + with io.open(source, 'r') as f: + config = yaml.load(f) + + # This allows get_help to execute in the right directory. + os.chdir(root) + + output = README_TMPL.render(config) + + with io.open(destination, 'w') as f: + f.write(output) + + +if __name__ == '__main__': + main() diff --git a/scripts/readme-gen/templates/README.tmpl.rst b/scripts/readme-gen/templates/README.tmpl.rst new file mode 100644 index 0000000..4fd2397 --- /dev/null +++ b/scripts/readme-gen/templates/README.tmpl.rst @@ -0,0 +1,87 @@ +{# The following line is a lie. BUT! Once jinja2 is done with it, it will + become truth! #} +.. This file is automatically generated. Do not edit this file directly. + +{{product.name}} Python Samples +=============================================================================== + +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/README.rst + + +This directory contains samples for {{product.name}}. {{product.description}} + +{{description}} + +.. _{{product.name}}: {{product.url}} + +{% if required_api_url %} +To run the sample, you need to enable the API at: {{required_api_url}} +{% endif %} + +{% if required_role %} +To run the sample, you need to have `{{required_role}}` role. +{% endif %} + +{{other_required_steps}} + +{% if setup %} +Setup +------------------------------------------------------------------------------- + +{% for section in setup %} + +{% include section + '.tmpl.rst' %} + +{% endfor %} +{% endif %} + +{% if samples %} +Samples +------------------------------------------------------------------------------- + +{% for sample in samples %} +{{sample.name}} ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +{% if not sample.hide_cloudshell_button %} +.. image:: https://gstatic.com/cloudssh/images/open-btn.png + :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/{{sample.file}},{{folder}}/README.rst +{% endif %} + + +{{sample.description}} + +To run this sample: + +.. code-block:: bash + + $ python {{sample.file}} +{% if sample.show_help %} + + {{get_help(sample.file)|indent}} +{% endif %} + + +{% endfor %} +{% endif %} + +{% if cloud_client_library %} + +The client library +------------------------------------------------------------------------------- + +This sample uses the `Google Cloud Client Library for Python`_. +You can read the documentation for more details on API usage and use GitHub +to `browse the source`_ and `report issues`_. + +.. _Google Cloud Client Library for Python: + https://googlecloudplatform.github.io/google-cloud-python/ +.. _browse the source: + https://github.com/GoogleCloudPlatform/google-cloud-python +.. _report issues: + https://github.com/GoogleCloudPlatform/google-cloud-python/issues + +{% endif %} + +.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/scripts/readme-gen/templates/auth.tmpl.rst b/scripts/readme-gen/templates/auth.tmpl.rst new file mode 100644 index 0000000..1446b94 --- /dev/null +++ b/scripts/readme-gen/templates/auth.tmpl.rst @@ -0,0 +1,9 @@ +Authentication +++++++++++++++ + +This sample requires you to have authentication setup. Refer to the +`Authentication Getting Started Guide`_ for instructions on setting up +credentials for applications. + +.. _Authentication Getting Started Guide: + https://cloud.google.com/docs/authentication/getting-started diff --git a/scripts/readme-gen/templates/auth_api_key.tmpl.rst b/scripts/readme-gen/templates/auth_api_key.tmpl.rst new file mode 100644 index 0000000..11957ce --- /dev/null +++ b/scripts/readme-gen/templates/auth_api_key.tmpl.rst @@ -0,0 +1,14 @@ +Authentication +++++++++++++++ + +Authentication for this service is done via an `API Key`_. To obtain an API +Key: + +1. Open the `Cloud Platform Console`_ +2. Make sure that billing is enabled for your project. +3. From the **Credentials** page, create a new **API Key** or use an existing + one for your project. + +.. _API Key: + https://developers.google.com/api-client-library/python/guide/aaa_apikeys +.. _Cloud Console: https://console.cloud.google.com/project?_ diff --git a/scripts/readme-gen/templates/install_deps.tmpl.rst b/scripts/readme-gen/templates/install_deps.tmpl.rst new file mode 100644 index 0000000..a0406db --- /dev/null +++ b/scripts/readme-gen/templates/install_deps.tmpl.rst @@ -0,0 +1,29 @@ +Install Dependencies +++++++++++++++++++++ + +#. Clone python-docs-samples and change directory to the sample directory you want to use. + + .. code-block:: bash + + $ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git + +#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions. + + .. _Python Development Environment Setup Guide: + https://cloud.google.com/python/setup + +#. Create a virtualenv. Samples are compatible with Python 2.7 and 3.4+. + + .. code-block:: bash + + $ virtualenv env + $ source env/bin/activate + +#. Install the dependencies needed to run the samples. + + .. code-block:: bash + + $ pip install -r requirements.txt + +.. _pip: https://pip.pypa.io/ +.. _virtualenv: https://virtualenv.pypa.io/ diff --git a/scripts/readme-gen/templates/install_portaudio.tmpl.rst b/scripts/readme-gen/templates/install_portaudio.tmpl.rst new file mode 100644 index 0000000..5ea33d1 --- /dev/null +++ b/scripts/readme-gen/templates/install_portaudio.tmpl.rst @@ -0,0 +1,35 @@ +Install PortAudio ++++++++++++++++++ + +Install `PortAudio`_. This is required by the `PyAudio`_ library to stream +audio from your computer's microphone. PyAudio depends on PortAudio for cross-platform compatibility, and is installed differently depending on the +platform. + +* For Mac OS X, you can use `Homebrew`_:: + + brew install portaudio + + **Note**: if you encounter an error when running `pip install` that indicates + it can't find `portaudio.h`, try running `pip install` with the following + flags:: + + pip install --global-option='build_ext' \ + --global-option='-I/usr/local/include' \ + --global-option='-L/usr/local/lib' \ + pyaudio + +* For Debian / Ubuntu Linux:: + + apt-get install portaudio19-dev python-all-dev + +* Windows may work without having to install PortAudio explicitly (it will get + installed with PyAudio). + +For more details, see the `PyAudio installation`_ page. + + +.. _PyAudio: https://people.csail.mit.edu/hubert/pyaudio/ +.. _PortAudio: http://www.portaudio.com/ +.. _PyAudio installation: + https://people.csail.mit.edu/hubert/pyaudio/#downloads +.. _Homebrew: http://brew.sh diff --git a/setup.py b/setup.py index 179bd6b..1ee6e5e 100644 --- a/setup.py +++ b/setup.py @@ -1,82 +1,60 @@ -# Copyright 2018 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. # You may obtain a copy of the License at # -# https://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# import io import os +import setuptools # type: ignore -import setuptools - -# Package metadata. - -name = "google-cloud-org-policy" -description = "Google Cloud Org Policy Protobufs" -version = "0.1.2" -# Should be one of: -# 'Development Status :: 3 - Alpha' -# 'Development Status :: 4 - Beta' -# 'Development Status :: 5 - Production/Stable' -release_status = "Development Status :: 4 - Beta" -dependencies = [ - "google-api-core[grpc] >= 1.14.0, < 2.0.0dev", -] - -# Setup boilerplate below this line. +version = "0.2.0" package_root = os.path.abspath(os.path.dirname(__file__)) -readme_filename = os.path.join(package_root, "README.md") +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") -] - -# Determine which namespaces are needed. -namespaces = ["google"] -if "google.cloud" in packages: - namespaces.append("google.cloud") setuptools.setup( - name=name, + name="google-cloud-org-policy", version=version, - description=description, long_description=readme, author="Google LLC", author_email="googleapis-packages@google.com", license="Apache 2.0", - url="https://github.com/googleapis/python-org-policy", + url="https://github.com/googleapis/python-orgpolicy", + packages=setuptools.PEP420PackageFinder.find(), + namespace_packages=("google", "google.cloud"), + platforms="Posix; MacOS X; Windows", + include_package_data=True, + install_requires=( + "google-api-core[grpc] >= 1.22.2, < 2.0.0dev", + "proto-plus >= 1.10.0", + ), + python_requires=">=3.6", classifiers=[ - release_status, + "Development Status :: 4 - Beta", "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", + "Operating System :: OS Independent", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", - "Operating System :: OS Independent", + "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, - python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*", - include_package_data=True, zip_safe=False, -) \ No newline at end of file +) diff --git a/synth.metadata b/synth.metadata index 1cc686d..da29da1 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,14 +4,40 @@ "git": { "name": ".", "remote": "git@github.com:googleapis/python-org-policy.git", - "sha": "316d35f8985e2239acdce078aec24780bebdd14e" + "sha": "4797e6d354848feb3b88f718c5e676929c9e4b5c" + } + }, + { + "git": { + "name": "googleapis", + "remote": "https://github.com/googleapis/googleapis.git", + "sha": "c06bbe28cc7287a55bf7926ee48da2565854de7f", + "internalRef": "359364666" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "be74d3e532faa47eb59f1a0eaebde0860d1d8ab4" + "sha": "79ab0b44a2cc7d803d07c107f9faf07729fc4012" + } + }, + { + "git": { + "name": "synthtool", + "remote": "https://github.com/googleapis/synthtool.git", + "sha": "79ab0b44a2cc7d803d07c107f9faf07729fc4012" + } + } + ], + "destinations": [ + { + "client": { + "source": "googleapis", + "apiName": "orgpolicy", + "apiVersion": "v2", + "language": "python", + "generator": "bazel" } } ] diff --git a/synth.py b/synth.py index c7e164b..f8237bd 100644 --- a/synth.py +++ b/synth.py @@ -12,25 +12,89 @@ # See the License for the specific language governing permissions and # limitations under the License. +import re + import synthtool as s import synthtool.gcp as gcp from synthtool import tmp from synthtool.languages import python from synthtool.sources import git -versions = ["v1"] + +gapic = gcp.GAPICBazel() +common = gcp.CommonTemplates() + +versions = ["v2"] # this package also has v1 protos, see below for note + +# ---------------------------------------------------------------------------- +# Generate orgpolicy GAPIC layer +# ---------------------------------------------------------------------------- +for version in versions: + library = gapic.py_library( + service="orgpolicy", + version=version, + bazel_target=f"//google/cloud/orgpolicy/{version}:orgpolicy-{version}-py", + ) + s.move(library, excludes=["setup.py", "README.rst", "docs/index.rst"]) + +# Rename to google-cloud-org-policy +# TODO: use bazel option to rename package +s.replace( + "google/cloud/**/*", + "google-cloud-orgpolicy", + "google-cloud-org-policy" +) # ---------------------------------------------------------------------------- # Add templated files # ---------------------------------------------------------------------------- -common = gcp.CommonTemplates() -templated_files = common.py_library() + +# coverage is 97 to exclude orgpolicy/v1 code +templated_files = common.py_library(microgenerator=True, cov_level=97) s.move( - templated_files, excludes=["noxfile.py", ".coveragerc", ".gitignore",], + templated_files, excludes=[".coveragerc",] ) -s.shell.run(["nox", "-s", "blacken"], hide_output=False) +# NOTE: This library also has legacy pb2.py files for "v1" +# in google/cloud/orgpolicy/v1 +# v1 only has messages (no service or RPCs). +# v1 protos can be refreshed by running +# `nox -s generate_protos`. See the noxfile.py. -# Add license headers -python.fix_pb2_headers() -python.fix_pb2_grpc_headers() +# Append generate_protos nox session +s.replace( + "noxfile.py", + """(@nox\.session.+? +def lint\(.+?)""", + '''@nox.session(python="3.8") +def generate_protos(session): + """Generates the protos using protoc. + + Some notes on the `google` directory: + 1. The `_pb2.py` files are produced by protoc. + 2. The .proto files are non-functional but are left in the repository + to make it easier to understand diffs. + 3. The `google` directory also has `__init__.py` files to create proper modules. + If a new subdirectory is added, you will need to create more `__init__.py` + files. + + NOTE: This is a hack and only runnable locally. You will need to have + the api-common-protos repo cloned. This should be migrated to use + bazel in the future. + """ + session.install("grpcio-tools") + protos = [str(p) for p in (Path(".").glob("google/**/*.proto"))] + + session.run( + "python", + "-m", + "grpc_tools.protoc", + "--proto_path=../api-common-protos", + "--proto_path=.", + "--python_out=.", + *protos, + ) + +\g<1>''', +) +s.shell.run(["nox", "-s", "blacken"], hide_output=False) diff --git a/testing/.gitignore b/testing/.gitignore new file mode 100644 index 0000000..b05fbd6 --- /dev/null +++ b/testing/.gitignore @@ -0,0 +1,3 @@ +test-env.sh +service-account.json +client-secrets.json \ No newline at end of file diff --git a/tests/unit/gapic/orgpolicy_v2/__init__.py b/tests/unit/gapic/orgpolicy_v2/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tests/unit/gapic/orgpolicy_v2/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/unit/gapic/orgpolicy_v2/test_org_policy.py b/tests/unit/gapic/orgpolicy_v2/test_org_policy.py new file mode 100644 index 0000000..8a1417b --- /dev/null +++ b/tests/unit/gapic/orgpolicy_v2/test_org_policy.py @@ -0,0 +1,2532 @@ +# -*- 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 grpc +from grpc.experimental import aio +import math +import pytest +from proto.marshal.rules.dates import DurationRule, TimestampRule + +from google import auth +from google.api_core import client_options +from google.api_core import 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 +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.orgpolicy_v2.services.org_policy import OrgPolicyAsyncClient +from google.cloud.orgpolicy_v2.services.org_policy import OrgPolicyClient +from google.cloud.orgpolicy_v2.services.org_policy import pagers +from google.cloud.orgpolicy_v2.services.org_policy import transports +from google.cloud.orgpolicy_v2.types import constraint +from google.cloud.orgpolicy_v2.types import orgpolicy +from google.oauth2 import service_account +from google.protobuf import timestamp_pb2 as timestamp # type: ignore +from google.type import expr_pb2 as expr # type: ignore + + +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 OrgPolicyClient._get_default_mtls_endpoint(None) is None + assert OrgPolicyClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + OrgPolicyClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + OrgPolicyClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + OrgPolicyClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert OrgPolicyClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +def test_org_policy_client_from_service_account_info(): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = OrgPolicyClient.from_service_account_info(info) + assert client.transport._credentials == creds + + assert client.transport._host == "orgpolicy.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [OrgPolicyClient, OrgPolicyAsyncClient,]) +def test_org_policy_client_from_service_account_file(client_class): + creds = 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 + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + + assert client.transport._host == "orgpolicy.googleapis.com:443" + + +def test_org_policy_client_get_transport_class(): + transport = OrgPolicyClient.get_transport_class() + available_transports = [ + transports.OrgPolicyGrpcTransport, + ] + assert transport in available_transports + + transport = OrgPolicyClient.get_transport_class("grpc") + assert transport == transports.OrgPolicyGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (OrgPolicyClient, transports.OrgPolicyGrpcTransport, "grpc"), + ( + OrgPolicyAsyncClient, + transports.OrgPolicyGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + OrgPolicyClient, "DEFAULT_ENDPOINT", modify_default_endpoint(OrgPolicyClient) +) +@mock.patch.object( + OrgPolicyAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(OrgPolicyAsyncClient), +) +def test_org_policy_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(OrgPolicyClient, "get_transport_class") as gtc: + transport = transport_class(credentials=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(OrgPolicyClient, "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", + [ + (OrgPolicyClient, transports.OrgPolicyGrpcTransport, "grpc", "true"), + ( + OrgPolicyAsyncClient, + transports.OrgPolicyGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (OrgPolicyClient, transports.OrgPolicyGrpcTransport, "grpc", "false"), + ( + OrgPolicyAsyncClient, + transports.OrgPolicyGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + OrgPolicyClient, "DEFAULT_ENDPOINT", modify_default_endpoint(OrgPolicyClient) +) +@mock.patch.object( + OrgPolicyAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(OrgPolicyAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_org_policy_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", + [ + (OrgPolicyClient, transports.OrgPolicyGrpcTransport, "grpc"), + ( + OrgPolicyAsyncClient, + transports.OrgPolicyGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_org_policy_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", + [ + (OrgPolicyClient, transports.OrgPolicyGrpcTransport, "grpc"), + ( + OrgPolicyAsyncClient, + transports.OrgPolicyGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_org_policy_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_org_policy_client_client_options_from_dict(): + with mock.patch( + "google.cloud.orgpolicy_v2.services.org_policy.transports.OrgPolicyGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = OrgPolicyClient(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_constraints( + transport: str = "grpc", request_type=orgpolicy.ListConstraintsRequest +): + client = OrgPolicyClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_constraints), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.ListConstraintsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_constraints(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == orgpolicy.ListConstraintsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListConstraintsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_constraints_from_dict(): + test_list_constraints(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_constraints_async( + transport: str = "grpc_asyncio", request_type=orgpolicy.ListConstraintsRequest +): + client = OrgPolicyAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_constraints), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + orgpolicy.ListConstraintsResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_constraints(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == orgpolicy.ListConstraintsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListConstraintsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_constraints_async_from_dict(): + await test_list_constraints_async(request_type=dict) + + +def test_list_constraints_field_headers(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.ListConstraintsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_constraints), "__call__") as call: + call.return_value = orgpolicy.ListConstraintsResponse() + + client.list_constraints(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", "parent=parent/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_list_constraints_field_headers_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.ListConstraintsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_constraints), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + orgpolicy.ListConstraintsResponse() + ) + + await client.list_constraints(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", "parent=parent/value",) in kw["metadata"] + + +def test_list_constraints_flattened(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_constraints), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.ListConstraintsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_constraints(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_constraints_flattened_error(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.list_constraints( + orgpolicy.ListConstraintsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_constraints_flattened_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_constraints), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.ListConstraintsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + orgpolicy.ListConstraintsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_constraints(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_constraints_flattened_error_async(): + client = OrgPolicyAsyncClient(credentials=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_constraints( + orgpolicy.ListConstraintsRequest(), parent="parent_value", + ) + + +def test_list_constraints_pager(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_constraints), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + orgpolicy.ListConstraintsResponse( + constraints=[ + constraint.Constraint(), + constraint.Constraint(), + constraint.Constraint(), + ], + next_page_token="abc", + ), + orgpolicy.ListConstraintsResponse(constraints=[], next_page_token="def",), + orgpolicy.ListConstraintsResponse( + constraints=[constraint.Constraint(),], next_page_token="ghi", + ), + orgpolicy.ListConstraintsResponse( + constraints=[constraint.Constraint(), constraint.Constraint(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_constraints(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, constraint.Constraint) for i in results) + + +def test_list_constraints_pages(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_constraints), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + orgpolicy.ListConstraintsResponse( + constraints=[ + constraint.Constraint(), + constraint.Constraint(), + constraint.Constraint(), + ], + next_page_token="abc", + ), + orgpolicy.ListConstraintsResponse(constraints=[], next_page_token="def",), + orgpolicy.ListConstraintsResponse( + constraints=[constraint.Constraint(),], next_page_token="ghi", + ), + orgpolicy.ListConstraintsResponse( + constraints=[constraint.Constraint(), constraint.Constraint(),], + ), + RuntimeError, + ) + pages = list(client.list_constraints(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_constraints_async_pager(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_constraints), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + orgpolicy.ListConstraintsResponse( + constraints=[ + constraint.Constraint(), + constraint.Constraint(), + constraint.Constraint(), + ], + next_page_token="abc", + ), + orgpolicy.ListConstraintsResponse(constraints=[], next_page_token="def",), + orgpolicy.ListConstraintsResponse( + constraints=[constraint.Constraint(),], next_page_token="ghi", + ), + orgpolicy.ListConstraintsResponse( + constraints=[constraint.Constraint(), constraint.Constraint(),], + ), + RuntimeError, + ) + async_pager = await client.list_constraints(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, constraint.Constraint) for i in responses) + + +@pytest.mark.asyncio +async def test_list_constraints_async_pages(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_constraints), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + orgpolicy.ListConstraintsResponse( + constraints=[ + constraint.Constraint(), + constraint.Constraint(), + constraint.Constraint(), + ], + next_page_token="abc", + ), + orgpolicy.ListConstraintsResponse(constraints=[], next_page_token="def",), + orgpolicy.ListConstraintsResponse( + constraints=[constraint.Constraint(),], next_page_token="ghi", + ), + orgpolicy.ListConstraintsResponse( + constraints=[constraint.Constraint(), constraint.Constraint(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_constraints(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_list_policies( + transport: str = "grpc", request_type=orgpolicy.ListPoliciesRequest +): + client = OrgPolicyClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_policies), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.ListPoliciesResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_policies(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == orgpolicy.ListPoliciesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListPoliciesPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_policies_from_dict(): + test_list_policies(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_policies_async( + transport: str = "grpc_asyncio", request_type=orgpolicy.ListPoliciesRequest +): + client = OrgPolicyAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_policies), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + orgpolicy.ListPoliciesResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_policies(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == orgpolicy.ListPoliciesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListPoliciesAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_policies_async_from_dict(): + await test_list_policies_async(request_type=dict) + + +def test_list_policies_field_headers(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.ListPoliciesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_policies), "__call__") as call: + call.return_value = orgpolicy.ListPoliciesResponse() + + client.list_policies(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", "parent=parent/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_list_policies_field_headers_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.ListPoliciesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_policies), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + orgpolicy.ListPoliciesResponse() + ) + + await client.list_policies(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", "parent=parent/value",) in kw["metadata"] + + +def test_list_policies_flattened(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_policies), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.ListPoliciesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_policies(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_policies_flattened_error(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.list_policies( + orgpolicy.ListPoliciesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_policies_flattened_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_policies), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.ListPoliciesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + orgpolicy.ListPoliciesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_policies(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_policies_flattened_error_async(): + client = OrgPolicyAsyncClient(credentials=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_policies( + orgpolicy.ListPoliciesRequest(), parent="parent_value", + ) + + +def test_list_policies_pager(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_policies), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(), orgpolicy.Policy(), orgpolicy.Policy(),], + next_page_token="abc", + ), + orgpolicy.ListPoliciesResponse(policies=[], next_page_token="def",), + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(),], next_page_token="ghi", + ), + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(), orgpolicy.Policy(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_policies(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, orgpolicy.Policy) for i in results) + + +def test_list_policies_pages(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_policies), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(), orgpolicy.Policy(), orgpolicy.Policy(),], + next_page_token="abc", + ), + orgpolicy.ListPoliciesResponse(policies=[], next_page_token="def",), + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(),], next_page_token="ghi", + ), + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(), orgpolicy.Policy(),], + ), + RuntimeError, + ) + pages = list(client.list_policies(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_policies_async_pager(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_policies), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(), orgpolicy.Policy(), orgpolicy.Policy(),], + next_page_token="abc", + ), + orgpolicy.ListPoliciesResponse(policies=[], next_page_token="def",), + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(),], next_page_token="ghi", + ), + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(), orgpolicy.Policy(),], + ), + RuntimeError, + ) + async_pager = await client.list_policies(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, orgpolicy.Policy) for i in responses) + + +@pytest.mark.asyncio +async def test_list_policies_async_pages(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_policies), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(), orgpolicy.Policy(), orgpolicy.Policy(),], + next_page_token="abc", + ), + orgpolicy.ListPoliciesResponse(policies=[], next_page_token="def",), + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(),], next_page_token="ghi", + ), + orgpolicy.ListPoliciesResponse( + policies=[orgpolicy.Policy(), orgpolicy.Policy(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_policies(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_policy(transport: str = "grpc", request_type=orgpolicy.GetPolicyRequest): + client = OrgPolicyClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy(name="name_value",) + + response = client.get_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] == orgpolicy.GetPolicyRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, orgpolicy.Policy) + + assert response.name == "name_value" + + +def test_get_policy_from_dict(): + test_get_policy(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_policy_async( + transport: str = "grpc_asyncio", request_type=orgpolicy.GetPolicyRequest +): + client = OrgPolicyAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + orgpolicy.Policy(name="name_value",) + ) + + response = await client.get_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == orgpolicy.GetPolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, orgpolicy.Policy) + + assert response.name == "name_value" + + +@pytest.mark.asyncio +async def test_get_policy_async_from_dict(): + await test_get_policy_async(request_type=dict) + + +def test_get_policy_field_headers(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.GetPolicyRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_policy), "__call__") as call: + call.return_value = orgpolicy.Policy() + + client.get_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", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_policy_field_headers_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.GetPolicyRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(orgpolicy.Policy()) + + await client.get_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", "name=name/value",) in kw["metadata"] + + +def test_get_policy_flattened(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_policy(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_policy_flattened_error(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_policy( + orgpolicy.GetPolicyRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_policy_flattened_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(orgpolicy.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_policy(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_policy_flattened_error_async(): + client = OrgPolicyAsyncClient(credentials=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_policy( + orgpolicy.GetPolicyRequest(), name="name_value", + ) + + +def test_get_effective_policy( + transport: str = "grpc", request_type=orgpolicy.GetEffectivePolicyRequest +): + client = OrgPolicyClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_effective_policy), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy(name="name_value",) + + response = client.get_effective_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] == orgpolicy.GetEffectivePolicyRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, orgpolicy.Policy) + + assert response.name == "name_value" + + +def test_get_effective_policy_from_dict(): + test_get_effective_policy(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_effective_policy_async( + transport: str = "grpc_asyncio", request_type=orgpolicy.GetEffectivePolicyRequest +): + client = OrgPolicyAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_effective_policy), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + orgpolicy.Policy(name="name_value",) + ) + + response = await client.get_effective_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == orgpolicy.GetEffectivePolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, orgpolicy.Policy) + + assert response.name == "name_value" + + +@pytest.mark.asyncio +async def test_get_effective_policy_async_from_dict(): + await test_get_effective_policy_async(request_type=dict) + + +def test_get_effective_policy_field_headers(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.GetEffectivePolicyRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_effective_policy), "__call__" + ) as call: + call.return_value = orgpolicy.Policy() + + client.get_effective_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", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_effective_policy_field_headers_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.GetEffectivePolicyRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_effective_policy), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(orgpolicy.Policy()) + + await client.get_effective_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", "name=name/value",) in kw["metadata"] + + +def test_get_effective_policy_flattened(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_effective_policy), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_effective_policy(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_effective_policy_flattened_error(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.get_effective_policy( + orgpolicy.GetEffectivePolicyRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_effective_policy_flattened_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_effective_policy), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(orgpolicy.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_effective_policy(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_effective_policy_flattened_error_async(): + client = OrgPolicyAsyncClient(credentials=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_effective_policy( + orgpolicy.GetEffectivePolicyRequest(), name="name_value", + ) + + +def test_create_policy( + transport: str = "grpc", request_type=orgpolicy.CreatePolicyRequest +): + client = OrgPolicyClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy(name="name_value",) + + response = client.create_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] == orgpolicy.CreatePolicyRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, orgpolicy.Policy) + + assert response.name == "name_value" + + +def test_create_policy_from_dict(): + test_create_policy(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_policy_async( + transport: str = "grpc_asyncio", request_type=orgpolicy.CreatePolicyRequest +): + client = OrgPolicyAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + orgpolicy.Policy(name="name_value",) + ) + + response = await client.create_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == orgpolicy.CreatePolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, orgpolicy.Policy) + + assert response.name == "name_value" + + +@pytest.mark.asyncio +async def test_create_policy_async_from_dict(): + await test_create_policy_async(request_type=dict) + + +def test_create_policy_field_headers(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.CreatePolicyRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_policy), "__call__") as call: + call.return_value = orgpolicy.Policy() + + client.create_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", "parent=parent/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_create_policy_field_headers_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.CreatePolicyRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(orgpolicy.Policy()) + + await client.create_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", "parent=parent/value",) in kw["metadata"] + + +def test_create_policy_flattened(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_policy( + parent="parent_value", policy=orgpolicy.Policy(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].parent == "parent_value" + + assert args[0].policy == orgpolicy.Policy(name="name_value") + + +def test_create_policy_flattened_error(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.create_policy( + orgpolicy.CreatePolicyRequest(), + parent="parent_value", + policy=orgpolicy.Policy(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_policy_flattened_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(orgpolicy.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_policy( + parent="parent_value", policy=orgpolicy.Policy(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].parent == "parent_value" + + assert args[0].policy == orgpolicy.Policy(name="name_value") + + +@pytest.mark.asyncio +async def test_create_policy_flattened_error_async(): + client = OrgPolicyAsyncClient(credentials=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_policy( + orgpolicy.CreatePolicyRequest(), + parent="parent_value", + policy=orgpolicy.Policy(name="name_value"), + ) + + +def test_update_policy( + transport: str = "grpc", request_type=orgpolicy.UpdatePolicyRequest +): + client = OrgPolicyClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy(name="name_value",) + + response = client.update_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] == orgpolicy.UpdatePolicyRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, orgpolicy.Policy) + + assert response.name == "name_value" + + +def test_update_policy_from_dict(): + test_update_policy(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_policy_async( + transport: str = "grpc_asyncio", request_type=orgpolicy.UpdatePolicyRequest +): + client = OrgPolicyAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + orgpolicy.Policy(name="name_value",) + ) + + response = await client.update_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == orgpolicy.UpdatePolicyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, orgpolicy.Policy) + + assert response.name == "name_value" + + +@pytest.mark.asyncio +async def test_update_policy_async_from_dict(): + await test_update_policy_async(request_type=dict) + + +def test_update_policy_field_headers(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.UpdatePolicyRequest() + request.policy.name = "policy.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_policy), "__call__") as call: + call.return_value = orgpolicy.Policy() + + client.update_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", "policy.name=policy.name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_policy_field_headers_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.UpdatePolicyRequest() + request.policy.name = "policy.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(orgpolicy.Policy()) + + await client.update_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", "policy.name=policy.name/value",) in kw["metadata"] + + +def test_update_policy_flattened(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_policy(policy=orgpolicy.Policy(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].policy == orgpolicy.Policy(name="name_value") + + +def test_update_policy_flattened_error(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.update_policy( + orgpolicy.UpdatePolicyRequest(), policy=orgpolicy.Policy(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_update_policy_flattened_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = orgpolicy.Policy() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(orgpolicy.Policy()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_policy( + policy=orgpolicy.Policy(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].policy == orgpolicy.Policy(name="name_value") + + +@pytest.mark.asyncio +async def test_update_policy_flattened_error_async(): + client = OrgPolicyAsyncClient(credentials=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_policy( + orgpolicy.UpdatePolicyRequest(), policy=orgpolicy.Policy(name="name_value"), + ) + + +def test_delete_policy( + transport: str = "grpc", request_type=orgpolicy.DeletePolicyRequest +): + client = OrgPolicyClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_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] == orgpolicy.DeletePolicyRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_policy_from_dict(): + test_delete_policy(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_policy_async( + transport: str = "grpc_asyncio", request_type=orgpolicy.DeletePolicyRequest +): + client = OrgPolicyAsyncClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_policy(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == orgpolicy.DeletePolicyRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_policy_async_from_dict(): + await test_delete_policy_async(request_type=dict) + + +def test_delete_policy_field_headers(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.DeletePolicyRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_policy), "__call__") as call: + call.return_value = None + + client.delete_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", "name=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_delete_policy_field_headers_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = orgpolicy.DeletePolicyRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_policy), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_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", "name=name/value",) in kw["metadata"] + + +def test_delete_policy_flattened(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.delete_policy(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_policy_flattened_error(): + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.delete_policy( + orgpolicy.DeletePolicyRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_policy_flattened_async(): + client = OrgPolicyAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_policy), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.delete_policy(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_policy_flattened_error_async(): + client = OrgPolicyAsyncClient(credentials=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_policy( + orgpolicy.DeletePolicyRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.OrgPolicyGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = OrgPolicyClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.OrgPolicyGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = OrgPolicyClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.OrgPolicyGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = OrgPolicyClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.OrgPolicyGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = OrgPolicyClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.OrgPolicyGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.OrgPolicyGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.OrgPolicyGrpcTransport, transports.OrgPolicyGrpcAsyncIOTransport,], +) +def test_transport_adc(transport_class): + # Test default credentials are used if not provided. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transport_class() + adc.assert_called_once() + + +def test_transport_grpc_default(): + # A client should use the gRPC transport by default. + client = OrgPolicyClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.OrgPolicyGrpcTransport,) + + +def test_org_policy_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.OrgPolicyTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_org_policy_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.orgpolicy_v2.services.org_policy.transports.OrgPolicyTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.OrgPolicyTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_constraints", + "list_policies", + "get_policy", + "get_effective_policy", + "create_policy", + "update_policy", + "delete_policy", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_org_policy_base_transport_with_credentials_file(): + # Instantiate the base transport with a credentials file + with mock.patch.object( + auth, "load_credentials_from_file" + ) as load_creds, mock.patch( + "google.cloud.orgpolicy_v2.services.org_policy.transports.OrgPolicyTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.OrgPolicyTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=("https://www.googleapis.com/auth/cloud-platform",), + quota_project_id="octopus", + ) + + +def test_org_policy_base_transport_with_adc(): + # Test the default credentials are used if credentials and credentials_file are None. + with mock.patch.object(auth, "default") as adc, mock.patch( + "google.cloud.orgpolicy_v2.services.org_policy.transports.OrgPolicyTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.OrgPolicyTransport() + adc.assert_called_once() + + +def test_org_policy_auth_adc(): + # If no credentials are provided, we should use ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + OrgPolicyClient() + adc.assert_called_once_with( + scopes=("https://www.googleapis.com/auth/cloud-platform",), + quota_project_id=None, + ) + + +def test_org_policy_transport_auth_adc(): + # If credentials and host are not provided, the transport class should use + # ADC credentials. + with mock.patch.object(auth, "default") as adc: + adc.return_value = (credentials.AnonymousCredentials(), None) + transports.OrgPolicyGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=("https://www.googleapis.com/auth/cloud-platform",), + quota_project_id="octopus", + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.OrgPolicyGrpcTransport, transports.OrgPolicyGrpcAsyncIOTransport], +) +def test_org_policy_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_org_policy_host_no_port(): + client = OrgPolicyClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="orgpolicy.googleapis.com" + ), + ) + assert client.transport._host == "orgpolicy.googleapis.com:443" + + +def test_org_policy_host_with_port(): + client = OrgPolicyClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="orgpolicy.googleapis.com:8000" + ), + ) + assert client.transport._host == "orgpolicy.googleapis.com:8000" + + +def test_org_policy_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.OrgPolicyGrpcTransport( + 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_org_policy_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.OrgPolicyGrpcAsyncIOTransport( + 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.OrgPolicyGrpcTransport, transports.OrgPolicyGrpcAsyncIOTransport], +) +def test_org_policy_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 = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(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=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.OrgPolicyGrpcTransport, transports.OrgPolicyGrpcAsyncIOTransport], +) +def test_org_policy_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=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_constraint_path(): + project = "squid" + constraint = "clam" + + expected = "projects/{project}/constraints/{constraint}".format( + project=project, constraint=constraint, + ) + actual = OrgPolicyClient.constraint_path(project, constraint) + assert expected == actual + + +def test_parse_constraint_path(): + expected = { + "project": "whelk", + "constraint": "octopus", + } + path = OrgPolicyClient.constraint_path(**expected) + + # Check that the path construction is reversible. + actual = OrgPolicyClient.parse_constraint_path(path) + assert expected == actual + + +def test_policy_path(): + project = "oyster" + policy = "nudibranch" + + expected = "projects/{project}/policies/{policy}".format( + project=project, policy=policy, + ) + actual = OrgPolicyClient.policy_path(project, policy) + assert expected == actual + + +def test_parse_policy_path(): + expected = { + "project": "cuttlefish", + "policy": "mussel", + } + path = OrgPolicyClient.policy_path(**expected) + + # Check that the path construction is reversible. + actual = OrgPolicyClient.parse_policy_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "winkle" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = OrgPolicyClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nautilus", + } + path = OrgPolicyClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = OrgPolicyClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "scallop" + + expected = "folders/{folder}".format(folder=folder,) + actual = OrgPolicyClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "abalone", + } + path = OrgPolicyClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = OrgPolicyClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "squid" + + expected = "organizations/{organization}".format(organization=organization,) + actual = OrgPolicyClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "clam", + } + path = OrgPolicyClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = OrgPolicyClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "whelk" + + expected = "projects/{project}".format(project=project,) + actual = OrgPolicyClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "octopus", + } + path = OrgPolicyClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = OrgPolicyClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "oyster" + location = "nudibranch" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = OrgPolicyClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + } + path = OrgPolicyClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = OrgPolicyClient.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.OrgPolicyTransport, "_prep_wrapped_messages" + ) as prep: + client = OrgPolicyClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.OrgPolicyTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = OrgPolicyClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info)