{% extends "_base.py.j2" %}

{% block content %}
{% import "%namespace/%name_%version/%sub/services/%service/_client_macros.j2" as macros %}
{% import "%namespace/%name_%version/%sub/services/%service/_shared_macros.j2" as shared_macros %}

import logging as std_logging
from collections import OrderedDict
import re
from typing import Dict, Callable, Mapping, MutableMapping, MutableSequence, Optional, {% if service.any_server_streaming %}AsyncIterable, Awaitable, {% endif %}{% if service.any_client_streaming %}AsyncIterator, {% endif %}Sequence, Tuple, Type, Union
{% if api.all_method_settings.values()|map(attribute="auto_populated_fields", default=[])|list %}
import uuid
{% endif %}
{% if service.any_deprecated %}
import warnings
{% endif %}

{% set package_path = api.naming.module_namespace|join('.') + "." + api.naming.versioned_module_name %}
from {{package_path}} import gapic_version as package_version

from google.api_core.client_options import ClientOptions
from google.api_core import exceptions as core_exceptions
from google.api_core import gapic_v1
from google.api_core import retry_async as retries
from google.auth import credentials as ga_credentials   # type: ignore
from google.oauth2 import service_account              # type: ignore
import google.protobuf

{{ shared_macros.add_google_api_core_version_header_import(service.version) }}
try:
    OptionalRetry = Union[retries.AsyncRetry, gapic_v1.method._MethodDefault, None]
except AttributeError:  # pragma: NO COVER
    OptionalRetry = Union[retries.AsyncRetry, object, None]  # type: ignore

{% filter sort_lines %}
{% for method in service.methods.values() %}
{% for ref_type in method.flat_ref_types %}
{{ ref_type.ident.python_import }}
{% endfor %}
{% endfor %}
{# TODO: Remove after https://github.com/googleapis/gapic-generator-python/pull/1240 is merged. #}
{% if opts.add_iam_methods or api.has_iam_mixin %}
from google.iam.v1 import iam_policy_pb2  # type: ignore
from google.iam.v1 import policy_pb2  # type: ignore
{% endif %}
{% if api.has_location_mixin %}
from google.cloud.location import locations_pb2 # type: ignore
{% endif %}
{% if api.has_operations_mixin %}
from google.longrunning import operations_pb2 # type: ignore
{% endif %}
{% endfilter %}
from .transports.base import {{ service.name }}Transport, DEFAULT_CLIENT_INFO
from .transports.grpc_asyncio import {{ service.grpc_asyncio_transport_name }}
from .client import {{ service.client_name }}

try:
    from google.api_core import client_logging  # type: ignore
    CLIENT_LOGGING_SUPPORTED = True  # pragma: NO COVER
except ImportError:  # pragma: NO COVER
    CLIENT_LOGGING_SUPPORTED = False

_LOGGER = std_logging.getLogger(__name__)

{# TODO(yon-mg): handle rest transport async client interaction #}
class {{ service.async_client_name }}:
    """{{ service.meta.doc|rst(width=72, indent=4) }}{% if service.version|length %}
    This class implements API version {{ service.version }}.{% endif %}"""

    _client: {{ service.client_name }}

    # Copy defaults from the synchronous client for use here.
    # Note: DEFAULT_ENDPOINT is deprecated. Use _DEFAULT_ENDPOINT_TEMPLATE instead.
    DEFAULT_ENDPOINT = {{ service.client_name }}.DEFAULT_ENDPOINT
    DEFAULT_MTLS_ENDPOINT = {{ service.client_name }}.DEFAULT_MTLS_ENDPOINT
    _DEFAULT_ENDPOINT_TEMPLATE = {{ service.client_name }}._DEFAULT_ENDPOINT_TEMPLATE
    _DEFAULT_UNIVERSE = {{ service.client_name }}._DEFAULT_UNIVERSE

    {% for message in service.resource_messages|sort(attribute="resource_type") %}
    {{ message.resource_type|snake_case }}_path = staticmethod({{ service.client_name }}.{{ message.resource_type|snake_case }}_path)
    parse_{{ message.resource_type|snake_case}}_path = staticmethod({{ service.client_name }}.parse_{{ message.resource_type|snake_case }}_path)
    {% endfor %}
    {% for resource_msg in service.common_resources.values()|sort(attribute="type_name") %}
    common_{{ resource_msg.message_type.resource_type|snake_case }}_path = staticmethod({{ service.client_name }}.common_{{ resource_msg.message_type.resource_type|snake_case }}_path)
    parse_common_{{ resource_msg.message_type.resource_type|snake_case }}_path = staticmethod({{ service.client_name }}.parse_common_{{ resource_msg.message_type.resource_type|snake_case }}_path)
    {% endfor %}

    @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:
            {{ service.async_client_name }}: The constructed client.
        """
        sa_info_func = (
            {{ service.client_name }}.from_service_account_info.__func__  # type: ignore
        )
        return sa_info_func({{ service.async_client_name }}, info, *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:
            {{ service.async_client_name }}: The constructed client.
        """
        sa_file_func = (
            {{ service.client_name }}.from_service_account_file.__func__  # type: ignore
        )
        return sa_file_func({{ service.async_client_name }}, filename, *args, **kwargs)

    from_service_account_json = from_service_account_file

    @classmethod
    def get_mtls_endpoint_and_cert_source(cls, client_options: Optional[ClientOptions] = None):
        """Return the API endpoint and client cert source for mutual TLS.

        The client cert source is determined in the following order:
        (1) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is not "true", the
        client cert source is None.
        (2) if `client_options.client_cert_source` is provided, use the provided one; if the
        default client cert source exists, use the default one; otherwise the client cert
        source is None.

        The API endpoint is determined in the following order:
        (1) if `client_options.api_endpoint` if provided, use the provided one.
        (2) if `GOOGLE_API_USE_CLIENT_CERTIFICATE` environment variable is "always", use the
        default mTLS endpoint; if the environment variable is "never", use the default API
        endpoint; otherwise if client cert source exists, use the default mTLS endpoint, otherwise
        use the default API endpoint.

        More details can be found at https://google.aip.dev/auth/4114.

        Args:
            client_options (google.api_core.client_options.ClientOptions): Custom options for the
                client. Only the `api_endpoint` and `client_cert_source` properties may be used
                in this method.

        Returns:
            Tuple[str, Callable[[], Tuple[bytes, bytes]]]: returns the API endpoint and the
                client cert source to use.

        Raises:
            google.auth.exceptions.MutualTLSChannelError: If any errors happen.
        """
        return {{ service.client_name }}.get_mtls_endpoint_and_cert_source(client_options)  # type: ignore

    @property
    def transport(self) -> {{ service.name }}Transport:
        """Returns the transport used by the client instance.

        Returns:
            {{ service.name }}Transport: The transport used by the client instance.
        """
        return self._client.transport
    
    @property
    def api_endpoint(self) -> str:
        """Return the API endpoint used by the client instance.
        
        Returns:
            str: The API endpoint used by the client instance.
        """
        return self._client._api_endpoint
    
    @property
    def universe_domain(self) -> str:
        """Return the universe domain used by the client instance.
        
        Returns:
            str: The universe domain used
                by the client instance.
        """
        return self._client._universe_domain

    get_transport_class = {{ service.client_name }}.get_transport_class

    def __init__(self, *,
            credentials: Optional[ga_credentials.Credentials] = None,
            transport: Optional[Union[str, {{ service.name }}Transport, Callable[..., {{ service.name }}Transport]]] = "grpc_asyncio",
            client_options: Optional[ClientOptions] = None,
            client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO,
            ) -> None:
        """Instantiates the {{ (service.async_client_name|snake_case).replace("_", " ") }}.

        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 (Optional[Union[str,{{ service.name }}Transport,Callable[..., {{ service.name }}Transport]]]):
                The transport to use, or a Callable that constructs and returns a new transport to use.
                If a Callable is given, it will be called with the same set of initialization
                arguments as used in the {{ service.name }}Transport constructor.
                If set to None, a transport is chosen automatically.
                {% if 'rest' in opts.transport and not opts.rest_numeric_enums %}
                {# TODO (gapic-generator-python/issues/1918): Remove the beta preview comment. #}
                NOTE: "rest" transport functionality is currently in a
                beta state (preview). We welcome your feedback via an
                issue in this library's source repository.
                {% endif %}
            client_options (Optional[Union[google.api_core.client_options.ClientOptions, dict]]):
                Custom options for the client.

                1. The ``api_endpoint`` property can be used to override the
                default endpoint provided by the client when ``transport`` is
                not explicitly provided. Only if this property is not set and
                ``transport`` was not explicitly provided, the endpoint is
                determined by the GOOGLE_API_USE_MTLS_ENDPOINT environment
                variable, which have one of the following values:
                "always" (always use the default mTLS endpoint), "never" (always
                use the default regular endpoint) and "auto" (auto-switch to the
                default mTLS endpoint if client certificate is present; this is
                the default value).

                2. If the GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable
                is "true", then the ``client_cert_source`` property can be used
                to provide a client certificate for mTLS transport. If
                not provided, the default SSL client certificate will be used if
                present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not
                set, no client certificate will be used.
                
                3. The ``universe_domain`` property can be used to override the
                default "googleapis.com" universe. Note that ``api_endpoint``
                property still takes precedence; and ``universe_domain`` is
                currently not supported for mTLS.

            client_info (google.api_core.gapic_v1.client_info.ClientInfo):
                The client info used to send a user-agent string along with
                API requests. If ``None``, then default info will be used.
                Generally, you only need to set this if you're developing
                your own client library.

        Raises:
            google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport
                creation failed for any reason.
        """
        {# NOTE(lidiz) Not using kwargs since we want the docstring and types. #}
        self._client = {{ service.client_name }}(
            credentials=credentials,
            transport=transport,
            client_options=client_options,
            client_info=client_info,

        )

        if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(std_logging.DEBUG):  # pragma: NO COVER
            _LOGGER.debug(
                "Created client `{{ service.meta.address.proto_package_versioned }}.{{ service.async_client_name }}`.",
                extra = {
                    "serviceName": "{{ service.meta.address.proto }}",
                    "universeDomain": getattr(self._client._transport._credentials, "universe_domain", ""),
                    "credentialsType": f"{type(self._client._transport._credentials).__module__}.{type(self._client._transport._credentials).__qualname__}",
                    "credentialsInfo": getattr(self.transport._credentials, "get_cred_info", lambda: None)(),
                } if hasattr(self._client._transport, "_credentials") else {
                    "serviceName": "{{ service.meta.address.proto }}",
                    "credentialsType": None,
                }
            )

    {% for method in service.methods.values() %}
      {% with method_name = method.client_method_name|snake_case + "_unary" if method.operation_service else method.client_method_name|snake_case %}
    {%+ if not method.server_streaming %}async {% endif %}def {{ method_name }}(self,
      {% endwith %}
            {% if not method.client_streaming %}
            request: Optional[Union[{{ method.input.ident }}, dict]] = None,
            *,
            {% for field in method.flattened_fields.values() %}
            {{ field.name }}: Optional[{{ field.ident }}] = None,
            {% endfor %}
            {% else %}
            requests: Optional[AsyncIterator[{{ method.input.ident }}]] = None,
            *,
            {% endif %}
            retry: OptionalRetry = gapic_v1.method.DEFAULT,
            timeout: Union[float, object] = gapic_v1.method.DEFAULT,
            {{ shared_macros.client_method_metadata_argument()|indent(8) }} = {{ shared_macros.client_method_metadata_default_value() }},
            {% if not method.server_streaming %}
            ) -> {{ method.client_output_async.ident }}:
            {% else %}
            ) -> Awaitable[AsyncIterable[{{ method.client_output_async.ident }}]]:
            {% endif %}
        r"""{{ method.meta.doc|rst(width=72, indent=8)|trim }}

        {% with snippet = snippet_index.get_snippet(service.name, method.name, sync=False) %}
        {% if snippet is not none %}
        .. code-block:: python

{{ snippet.full_snippet|indent(width=12, first=True) }}
        {% endif %}
        {% endwith %}

        Args:
            {% if not method.client_streaming %}
            request (Optional[Union[{{ method.input.ident.sphinx }}, dict]]):
                The request object.{{ " " }}
                {{- method.input.meta.doc|rst(width=72, indent=16, nl=False) }}
            {% for key, field in method.flattened_fields.items() %}
            {{ field.name }} (:class:`{{ field.ident.sphinx }}`):
                {{ field.meta.doc|rst(width=72, indent=16) }}
                This corresponds to the ``{{ key }}`` field
                on the ``request`` instance; if ``request`` is provided, this
                should not be set.
            {% endfor %}
            {% else %}
            requests (AsyncIterator[`{{ method.input.ident.sphinx }}`]):
                The request object AsyncIterator.{{ " " }}
                {{- method.input.meta.doc|rst(width=72, indent=16, nl=False) }}
            {% endif %}
            retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any,
                should be retried.
            timeout (float): The timeout for this request.
            {{ shared_macros.client_method_metadata_argument_doc()|indent(4) }}
        {% if not method.void %}

        Returns:
            {% if not method.server_streaming %}
            {{ method.client_output_async.ident.sphinx }}:
            {% else %}
            AsyncIterable[{{ method.client_output_async.ident.sphinx }}]:
            {% endif %}
                {{ method.client_output_async.meta.doc|rst(width=72, indent=16, source_format='rst') }}
        {% endif %}
        """
        {% if method.is_deprecated %}
        warnings.warn("{{ service.async_client_name }}.{{ method.name|snake_case }} is deprecated",
            DeprecationWarning)

        {% endif %}
        {% if not method.client_streaming %}
        # Create or coerce a protobuf request object.
        {% if method.flattened_fields %}
        # - Quick check: If we got a request object, we should *not* have
        #   gotten any keyword arguments that map to the request.
        flattened_params = [{{ method.flattened_fields.values()|join(", ", attribute="name") }}]
        has_flattened_params = len([param for param in flattened_params if param is not None]) > 0
        if request is not None and has_flattened_params:
            raise ValueError("If the `request` argument is set, then none of "
                             "the individual field arguments should be set.")

        {% endif %}
        {% if method.input.ident.package != method.ident.package %} {# request lives in a different package, so there is no proto wrapper #}
        # - The request isn't a proto-plus wrapped type,
        #   so it must be constructed via keyword expansion.
        if isinstance(request, dict):
            request = {{ method.input.ident }}(**request)
        elif not request:
            request = {{ method.input.ident }}({% if method.flattened_fields %}{% for f in method.flattened_fields.values() %}{{ f.name }}={{ f.name }}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %})
        {% else %}
        # - Use the request object if provided (there's no risk of modifying the input as
        #   there are no flattened fields), or create one.
        if not isinstance(request, {{ method.input.ident }}):
            request = {{ method.input.ident }}(request)
        {% endif %} {# different request package #}

        {# Vanilla python protobuf wrapper types cannot _set_ repeated fields #}
        {% if method.flattened_fields and method.input.ident.package == method.ident.package %}
        # If we have keyword arguments corresponding to fields on the
        # request, apply these.
        {% endif %}
        {% for key, field in method.flattened_fields.items() if not field.repeated and method.input.ident.package == method.ident.package %}
        if {{ field.name }} is not None:
            request.{{ key }} = {{ field.name }}
        {% endfor %}
        {# Map-y fields can be _updated_, however #}
        {% for key, field in method.flattened_fields.items() if field.map and method.input.ident.package == method.ident.package %}

        if {{ field.name }}:
            request.{{ key }}.update({{ field.name }})
        {% endfor %}
        {# And list-y fields can be _extended_ #}
        {% for key, field in method.flattened_fields.items() if field.repeated and not field.map and method.input.ident.package == method.ident.package %}
        if {{ field.name }}:
            request.{{ key }}.extend({{ field.name }})
        {% endfor %}
        {% endif %}

        # Wrap the RPC method; this adds retry and timeout information,
        # and friendly error handling.
        rpc = self._client._transport._wrapped_methods[self._client._transport.{{ method.transport_safe_name|snake_case }}]

{{ shared_macros.create_metadata(method) }}
{{ shared_macros.add_api_version_header_to_metadata(service.version) }}
{{ shared_macros.auto_populate_uuid4_fields(api, method) }}

        # Validate the universe domain.
        self._client._validate_universe_domain()

        # Send the request.
        {%+ if not method.void %}response = {% endif %}
        {% if not method.server_streaming %}await {% endif %}rpc(
            {% if not method.client_streaming %}
            request,
            {% else %}
            requests,
            {% endif %}
            retry=retry,
            timeout=timeout,
            metadata=metadata,
        )
        {% if method.lro %}

        # Wrap the response in an operation future.
        response = {{ method.client_output_async.ident.module_alias or method.client_output_async.ident.module }}.from_gapic(
            response,
            self._client._transport.operations_client,
            {{ method.lro.response_type.ident }},
            metadata_type={{ method.lro.metadata_type.ident }},
        )
        {% elif method.paged_result_field %}

        # This method is paged; wrap the response in a pager, which provides
        # an `__aiter__` convenience method.
        response = {{ method.client_output_async.ident }}(
            method=rpc,
            request=request,
            response=response,
            retry=retry,
            timeout=timeout,
            metadata=metadata,
        )
        {% endif %}
        {% if not method.void %}

        # Done; return the response.
        return response
        {% endif %}
        {{ "\n" }}
    {% endfor %}

    {% include '%namespace/%name_%version/%sub/services/%service/_async_mixins.py.j2' %}

    {# TODO: Remove after https://github.com/googleapis/gapic-generator-python/pull/1240 is merged. #}
    {% if opts.add_iam_methods %}
    async def set_iam_policy(
        self,
        request: Optional[Union[iam_policy_pb2.SetIamPolicyRequest, dict]] = None,
        *,
        retry: OptionalRetry = gapic_v1.method.DEFAULT,
        timeout: Union[float, object] = gapic_v1.method.DEFAULT,
        {{ shared_macros.client_method_metadata_argument()|indent(8) }} = {{ shared_macros.client_method_metadata_default_value() }},
    ) -> policy_pb2.Policy:
        r"""Sets the IAM access control policy on the specified function.

        Replaces any existing policy.

        Args:
            request (:class:`~.policy_pb2.SetIamPolicyRequest`):
                The request object. Request message for `SetIamPolicy`
                method.
            retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any,
                should be retried.
            timeout (float): The timeout for this request.
            {{ shared_macros.client_method_metadata_argument_doc()|indent(4) }}
        Returns:
            ~.policy_pb2.Policy:
                Defines an Identity and Access Management (IAM) policy.
                It is used to specify access control policies for Cloud
                Platform resources.
                A ``Policy`` is a collection of ``bindings``. A
                ``binding`` binds one or more ``members`` to a single
                ``role``. Members can be user accounts, service
                accounts, Google groups, and domains (such as G Suite).
                A ``role`` is a named list of permissions (defined by
                IAM or configured by users). A ``binding`` can
                optionally specify a ``condition``, which is a logic
                expression that further constrains the role binding
                based on attributes about the request and/or target
                resource.

                **JSON Example**

                ::
                    {
                      "bindings": [
                        {
                          "role": "roles/resourcemanager.organizationAdmin",
                          "members": [
                            "user:mike@example.com",
                            "group:admins@example.com",
                            "domain:google.com",
                            "serviceAccount:my-project-id@appspot.gserviceaccount.com"
                          ]
                        },
                        {
                          "role": "roles/resourcemanager.organizationViewer",
                          "members": ["user:eve@example.com"],
                          "condition": {
                            "title": "expirable access",
                            "description": "Does not grant access after Sep 2020",
                            "expression": "request.time <
                            timestamp('2020-10-01T00:00:00.000Z')",
                          }
                        }
                      ]
                    }

                **YAML Example**

                ::

                    bindings:
                    - members:
                      - user:mike@example.com
                      - group:admins@example.com
                      - domain:google.com
                      - serviceAccount:my-project-id@appspot.gserviceaccount.com
                      role: roles/resourcemanager.organizationAdmin
                    - members:
                      - user:eve@example.com
                      role: roles/resourcemanager.organizationViewer
                      condition:
                        title: expirable access
                        description: Does not grant access after Sep 2020
                        expression: request.time < timestamp('2020-10-01T00:00:00.000Z')

                For a description of IAM and its features, see the `IAM
                developer's
                guide <https://cloud.google.com/iam/docs>`__.
        """
        # Create or coerce a protobuf request object.

        # The request isn't a proto-plus wrapped type,
        # so it must be constructed via keyword expansion.
        if request is None:
            request_pb = iam_policy_pb2.SetIamPolicyRequest()
        elif isinstance(request, dict):
            request_pb = iam_policy_pb2.SetIamPolicyRequest(**request)
        else:
            request_pb = request

        # Wrap the RPC method; this adds retry and timeout information,
        # and friendly error handling.
        rpc = self.transport._wrapped_methods[self._client._transport.set_iam_policy]

        # Certain fields should be provided within the metadata header;
        # add these here.
        metadata = tuple(metadata) + (
            gapic_v1.routing_header.to_grpc_metadata((("resource", request_pb.resource),)),
        )

        # Validate the universe domain.
        self._client._validate_universe_domain()

        # Send the request.
        response = await rpc(request_pb, retry=retry, timeout=timeout, metadata=metadata,)

        # Done; return the response.
        return response

    async def get_iam_policy(
        self,
        request: Optional[Union[iam_policy_pb2.GetIamPolicyRequest, dict]] = None,
        *,
        retry: OptionalRetry = gapic_v1.method.DEFAULT,
        timeout: Union[float, object] = gapic_v1.method.DEFAULT,
        {{ shared_macros.client_method_metadata_argument()|indent(8) }} = {{ shared_macros.client_method_metadata_default_value() }},
    ) -> policy_pb2.Policy:
        r"""Gets the IAM access control policy for a function.

        Returns an empty policy if the function exists and does
        not have a policy set.

        Args:
            request (:class:`~.iam_policy_pb2.GetIamPolicyRequest`):
                The request object. Request message for `GetIamPolicy`
                method.
            retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any,
                should be retried.
            timeout (float): The timeout for this request.
            {{ shared_macros.client_method_metadata_argument_doc()|indent(4) }}
        Returns:
            ~.policy_pb2.Policy:
                Defines an Identity and Access Management (IAM) policy.
                It is used to specify access control policies for Cloud
                Platform resources.
                A ``Policy`` is a collection of ``bindings``. A
                ``binding`` binds one or more ``members`` to a single
                ``role``. Members can be user accounts, service
                accounts, Google groups, and domains (such as G Suite).
                A ``role`` is a named list of permissions (defined by
                IAM or configured by users). A ``binding`` can
                optionally specify a ``condition``, which is a logic
                expression that further constrains the role binding
                based on attributes about the request and/or target
                resource.

                **JSON Example**

                ::

                    {
                      "bindings": [
                        {
                          "role": "roles/resourcemanager.organizationAdmin",
                          "members": [
                            "user:mike@example.com",
                            "group:admins@example.com",
                            "domain:google.com",
                            "serviceAccount:my-project-id@appspot.gserviceaccount.com"
                          ]
                        },
                        {
                          "role": "roles/resourcemanager.organizationViewer",
                          "members": ["user:eve@example.com"],
                          "condition": {
                            "title": "expirable access",
                            "description": "Does not grant access after Sep 2020",
                            "expression": "request.time <
                            timestamp('2020-10-01T00:00:00.000Z')",
                          }
                        }
                      ]
                    }

                **YAML Example**

                ::

                    bindings:
                    - members:
                      - user:mike@example.com
                      - group:admins@example.com
                      - domain:google.com
                      - serviceAccount:my-project-id@appspot.gserviceaccount.com
                      role: roles/resourcemanager.organizationAdmin
                    - members:
                      - user:eve@example.com
                      role: roles/resourcemanager.organizationViewer
                      condition:
                        title: expirable access
                        description: Does not grant access after Sep 2020
                        expression: request.time < timestamp('2020-10-01T00:00:00.000Z')

                For a description of IAM and its features, see the `IAM
                developer's
                guide <https://cloud.google.com/iam/docs>`__.
        """
        # Create or coerce a protobuf request object.

        # The request isn't a proto-plus wrapped type,
        # so it must be constructed via keyword expansion.
        if request is None:
            request_pb = iam_policy_pb2.GetIamPolicyRequest()
        elif isinstance(request, dict):
            request_pb = iam_policy_pb2.GetIamPolicyRequest(**request)
        else:
            request_pb = request

        # Wrap the RPC method; this adds retry and timeout information,
        # and friendly error handling.
        rpc = self.transport._wrapped_methods[self._client._transport.get_iam_policy]

        # Certain fields should be provided within the metadata header;
        # add these here.
        metadata = tuple(metadata) + (
            gapic_v1.routing_header.to_grpc_metadata((("resource", request_pb.resource),)),
        )

        # Validate the universe domain.
        self._client._validate_universe_domain()

        # Send the request.
        response = await rpc(request_pb, retry=retry, timeout=timeout, metadata=metadata,)

        # Done; return the response.
        return response

    async def test_iam_permissions(
        self,
        request: Optional[Union[iam_policy_pb2.TestIamPermissionsRequest, dict]] = None,
        *,
        retry: OptionalRetry = gapic_v1.method.DEFAULT,
        timeout: Union[float, object] = gapic_v1.method.DEFAULT,
        {{ shared_macros.client_method_metadata_argument()|indent(8) }} = {{ shared_macros.client_method_metadata_default_value() }},
    ) -> iam_policy_pb2.TestIamPermissionsResponse:
        r"""Tests the specified permissions against the IAM access control
            policy for a function.

        If the function does not exist, this will
        return an empty set of permissions, not a NOT_FOUND error.

        Args:
            request (:class:`~.iam_policy_pb2.TestIamPermissionsRequest`):
                The request object. Request message for
                `TestIamPermissions` method.
            retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any,
                should be retried.
            timeout (float): The timeout for this request.
            {{ shared_macros.client_method_metadata_argument_doc()|indent(4) }}
        Returns:
            ~iam_policy_pb2.PolicyTestIamPermissionsResponse:
                Response message for ``TestIamPermissions`` method.
        """
        # Create or coerce a protobuf request object.

        # The request isn't a proto-plus wrapped type,
        # so it must be constructed via keyword expansion.
        if request is None:
            request_pb = iam_policy_pb2.TestIamPermissionsRequest()
        elif isinstance(request, dict):
            request_pb = iam_policy_pb2.TestIamPermissionsRequest(**request)
        else:
            request_pb = request

        # Wrap the RPC method; this adds retry and timeout information,
        # and friendly error handling.
        rpc = self.transport._wrapped_methods[self._client._transport.test_iam_permissions]

        # Certain fields should be provided within the metadata header;
        # add these here.
        metadata = tuple(metadata) + (
            gapic_v1.routing_header.to_grpc_metadata((("resource", request_pb.resource),)),
        )

        # Validate the universe domain.
        self._client._validate_universe_domain()

        # Send the request.
        response = await rpc(request_pb, retry=retry, timeout=timeout, metadata=metadata,)

        # Done; return the response.
        return response
    {% endif %}

    async def __aenter__(self) -> "{{ service.async_client_name }}":
        return self

    async def __aexit__(self, exc_type, exc, tb):
        await self.transport.close()

DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo(gapic_version=package_version.__version__)

if hasattr(DEFAULT_CLIENT_INFO, "protobuf_runtime_version"):   # pragma: NO COVER
    DEFAULT_CLIENT_INFO.protobuf_runtime_version = google.protobuf.__version__


__all__ = (
    "{{ service.async_client_name }}",
)
{% endblock %}
