diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..6ad83346 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,17 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.3.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml +- repo: https://github.com/psf/black + rev: 19.10b0 + hooks: + - id: black +- repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.4 + hooks: + - id: flake8 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 08864eac..cd40ffa7 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -111,6 +111,16 @@ Coding Style should point to the official ``googleapis`` checkout and the the branch should be the main branch on that remote (``master``). +- This repository contains configuration for the + `pre-commit `__ tool, which automates checking + our linters during a commit. If you have it installed on your ``$PATH``, + you can enable enforcing those checks via: + +.. code-block:: bash + + $ pre-commit install + pre-commit installed at .git/hooks/pre-commit + Exceptions to PEP8: - Many unit tests use a helper method, ``_call_fut`` ("FUT" is short for diff --git a/docs/conf.py b/docs/conf.py index dcc6e5f6..7610f0fc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -345,10 +345,10 @@ # 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), + "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.io/grpc/python/", None), + "grpc": ("https://grpc.github.io/grpc/python/", None), "proto-plus": ("https://proto-plus-python.readthedocs.io/en/latest/", None), } diff --git a/docs/dialogflowcx_v3/services.rst b/docs/dialogflowcx_v3/services.rst new file mode 100644 index 00000000..eaaea2c8 --- /dev/null +++ b/docs/dialogflowcx_v3/services.rst @@ -0,0 +1,39 @@ +Services for Google Cloud Dialogflowcx v3 API +============================================= + +.. automodule:: google.cloud.dialogflowcx_v3.services.agents + :members: + :inherited-members: +.. automodule:: google.cloud.dialogflowcx_v3.services.entity_types + :members: + :inherited-members: +.. automodule:: google.cloud.dialogflowcx_v3.services.environments + :members: + :inherited-members: +.. automodule:: google.cloud.dialogflowcx_v3.services.flows + :members: + :inherited-members: +.. automodule:: google.cloud.dialogflowcx_v3.services.intents + :members: + :inherited-members: +.. automodule:: google.cloud.dialogflowcx_v3.services.pages + :members: + :inherited-members: +.. automodule:: google.cloud.dialogflowcx_v3.services.security_settings_service + :members: + :inherited-members: +.. automodule:: google.cloud.dialogflowcx_v3.services.session_entity_types + :members: + :inherited-members: +.. automodule:: google.cloud.dialogflowcx_v3.services.sessions + :members: + :inherited-members: +.. automodule:: google.cloud.dialogflowcx_v3.services.transition_route_groups + :members: + :inherited-members: +.. automodule:: google.cloud.dialogflowcx_v3.services.versions + :members: + :inherited-members: +.. automodule:: google.cloud.dialogflowcx_v3.services.webhooks + :members: + :inherited-members: diff --git a/docs/dialogflowcx_v3/types.rst b/docs/dialogflowcx_v3/types.rst new file mode 100644 index 00000000..eee8146a --- /dev/null +++ b/docs/dialogflowcx_v3/types.rst @@ -0,0 +1,6 @@ +Types for Google Cloud Dialogflowcx v3 API +========================================== + +.. automodule:: google.cloud.dialogflowcx_v3.types + :members: + :show-inheritance: diff --git a/docs/dialogflowcx_v3beta1/types.rst b/docs/dialogflowcx_v3beta1/types.rst index c1ad770a..3c1160db 100644 --- a/docs/dialogflowcx_v3beta1/types.rst +++ b/docs/dialogflowcx_v3beta1/types.rst @@ -3,3 +3,4 @@ Types for Google Cloud Dialogflowcx v3beta1 API .. automodule:: google.cloud.dialogflowcx_v3beta1.types :members: + :show-inheritance: diff --git a/docs/index.rst b/docs/index.rst index 2d922582..6671a47b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -8,6 +8,8 @@ API Reference .. toctree:: :maxdepth: 2 + dialogflowcx_v3/services + dialogflowcx_v3/types dialogflowcx_v3beta1/services dialogflowcx_v3beta1/types diff --git a/google/cloud/dialogflowcx/__init__.py b/google/cloud/dialogflowcx/__init__.py index 1d9845af..44462fe6 100644 --- a/google/cloud/dialogflowcx/__init__.py +++ b/google/cloud/dialogflowcx/__init__.py @@ -15,209 +15,221 @@ # limitations under the License. # -from google.cloud.dialogflowcx_v3beta1.services.agents.async_client import ( - AgentsAsyncClient, -) -from google.cloud.dialogflowcx_v3beta1.services.agents.client import AgentsClient -from google.cloud.dialogflowcx_v3beta1.services.entity_types.async_client import ( +from google.cloud.dialogflowcx_v3.services.agents.async_client import AgentsAsyncClient +from google.cloud.dialogflowcx_v3.services.agents.client import AgentsClient +from google.cloud.dialogflowcx_v3.services.entity_types.async_client import ( EntityTypesAsyncClient, ) -from google.cloud.dialogflowcx_v3beta1.services.entity_types.client import ( - EntityTypesClient, -) -from google.cloud.dialogflowcx_v3beta1.services.environments.async_client import ( +from google.cloud.dialogflowcx_v3.services.entity_types.client import EntityTypesClient +from google.cloud.dialogflowcx_v3.services.environments.async_client import ( EnvironmentsAsyncClient, ) -from google.cloud.dialogflowcx_v3beta1.services.environments.client import ( - EnvironmentsClient, -) -from google.cloud.dialogflowcx_v3beta1.services.flows.async_client import ( - FlowsAsyncClient, -) -from google.cloud.dialogflowcx_v3beta1.services.flows.client import FlowsClient -from google.cloud.dialogflowcx_v3beta1.services.intents.async_client import ( +from google.cloud.dialogflowcx_v3.services.environments.client import EnvironmentsClient +from google.cloud.dialogflowcx_v3.services.flows.async_client import FlowsAsyncClient +from google.cloud.dialogflowcx_v3.services.flows.client import FlowsClient +from google.cloud.dialogflowcx_v3.services.intents.async_client import ( IntentsAsyncClient, ) -from google.cloud.dialogflowcx_v3beta1.services.intents.client import IntentsClient -from google.cloud.dialogflowcx_v3beta1.services.pages.async_client import ( - PagesAsyncClient, +from google.cloud.dialogflowcx_v3.services.intents.client import IntentsClient +from google.cloud.dialogflowcx_v3.services.pages.async_client import PagesAsyncClient +from google.cloud.dialogflowcx_v3.services.pages.client import PagesClient +from google.cloud.dialogflowcx_v3.services.security_settings_service.async_client import ( + SecuritySettingsServiceAsyncClient, +) +from google.cloud.dialogflowcx_v3.services.security_settings_service.client import ( + SecuritySettingsServiceClient, ) -from google.cloud.dialogflowcx_v3beta1.services.pages.client import PagesClient -from google.cloud.dialogflowcx_v3beta1.services.session_entity_types.async_client import ( +from google.cloud.dialogflowcx_v3.services.session_entity_types.async_client import ( SessionEntityTypesAsyncClient, ) -from google.cloud.dialogflowcx_v3beta1.services.session_entity_types.client import ( +from google.cloud.dialogflowcx_v3.services.session_entity_types.client import ( SessionEntityTypesClient, ) -from google.cloud.dialogflowcx_v3beta1.services.sessions.async_client import ( +from google.cloud.dialogflowcx_v3.services.sessions.async_client import ( SessionsAsyncClient, ) -from google.cloud.dialogflowcx_v3beta1.services.sessions.client import SessionsClient -from google.cloud.dialogflowcx_v3beta1.services.transition_route_groups.async_client import ( +from google.cloud.dialogflowcx_v3.services.sessions.client import SessionsClient +from google.cloud.dialogflowcx_v3.services.transition_route_groups.async_client import ( TransitionRouteGroupsAsyncClient, ) -from google.cloud.dialogflowcx_v3beta1.services.transition_route_groups.client import ( +from google.cloud.dialogflowcx_v3.services.transition_route_groups.client import ( TransitionRouteGroupsClient, ) -from google.cloud.dialogflowcx_v3beta1.services.versions.async_client import ( +from google.cloud.dialogflowcx_v3.services.versions.async_client import ( VersionsAsyncClient, ) -from google.cloud.dialogflowcx_v3beta1.services.versions.client import VersionsClient -from google.cloud.dialogflowcx_v3beta1.services.webhooks.async_client import ( +from google.cloud.dialogflowcx_v3.services.versions.client import VersionsClient +from google.cloud.dialogflowcx_v3.services.webhooks.async_client import ( WebhooksAsyncClient, ) -from google.cloud.dialogflowcx_v3beta1.services.webhooks.client import WebhooksClient -from google.cloud.dialogflowcx_v3beta1.types.agent import Agent -from google.cloud.dialogflowcx_v3beta1.types.agent import CreateAgentRequest -from google.cloud.dialogflowcx_v3beta1.types.agent import DeleteAgentRequest -from google.cloud.dialogflowcx_v3beta1.types.agent import ExportAgentRequest -from google.cloud.dialogflowcx_v3beta1.types.agent import ExportAgentResponse -from google.cloud.dialogflowcx_v3beta1.types.agent import GetAgentRequest -from google.cloud.dialogflowcx_v3beta1.types.agent import ListAgentsRequest -from google.cloud.dialogflowcx_v3beta1.types.agent import ListAgentsResponse -from google.cloud.dialogflowcx_v3beta1.types.agent import RestoreAgentRequest -from google.cloud.dialogflowcx_v3beta1.types.agent import SpeechToTextSettings -from google.cloud.dialogflowcx_v3beta1.types.agent import UpdateAgentRequest -from google.cloud.dialogflowcx_v3beta1.types.audio_config import AudioEncoding -from google.cloud.dialogflowcx_v3beta1.types.audio_config import InputAudioConfig -from google.cloud.dialogflowcx_v3beta1.types.audio_config import OutputAudioConfig -from google.cloud.dialogflowcx_v3beta1.types.audio_config import OutputAudioEncoding -from google.cloud.dialogflowcx_v3beta1.types.audio_config import SpeechModelVariant -from google.cloud.dialogflowcx_v3beta1.types.audio_config import SpeechWordInfo -from google.cloud.dialogflowcx_v3beta1.types.audio_config import SsmlVoiceGender -from google.cloud.dialogflowcx_v3beta1.types.audio_config import SynthesizeSpeechConfig -from google.cloud.dialogflowcx_v3beta1.types.audio_config import VoiceSelectionParams -from google.cloud.dialogflowcx_v3beta1.types.entity_type import CreateEntityTypeRequest -from google.cloud.dialogflowcx_v3beta1.types.entity_type import DeleteEntityTypeRequest -from google.cloud.dialogflowcx_v3beta1.types.entity_type import EntityType -from google.cloud.dialogflowcx_v3beta1.types.entity_type import GetEntityTypeRequest -from google.cloud.dialogflowcx_v3beta1.types.entity_type import ListEntityTypesRequest -from google.cloud.dialogflowcx_v3beta1.types.entity_type import ListEntityTypesResponse -from google.cloud.dialogflowcx_v3beta1.types.entity_type import UpdateEntityTypeRequest -from google.cloud.dialogflowcx_v3beta1.types.environment import CreateEnvironmentRequest -from google.cloud.dialogflowcx_v3beta1.types.environment import DeleteEnvironmentRequest -from google.cloud.dialogflowcx_v3beta1.types.environment import Environment -from google.cloud.dialogflowcx_v3beta1.types.environment import GetEnvironmentRequest -from google.cloud.dialogflowcx_v3beta1.types.environment import ListEnvironmentsRequest -from google.cloud.dialogflowcx_v3beta1.types.environment import ListEnvironmentsResponse -from google.cloud.dialogflowcx_v3beta1.types.environment import ( +from google.cloud.dialogflowcx_v3.services.webhooks.client import WebhooksClient +from google.cloud.dialogflowcx_v3.types.agent import Agent +from google.cloud.dialogflowcx_v3.types.agent import CreateAgentRequest +from google.cloud.dialogflowcx_v3.types.agent import DeleteAgentRequest +from google.cloud.dialogflowcx_v3.types.agent import ExportAgentRequest +from google.cloud.dialogflowcx_v3.types.agent import ExportAgentResponse +from google.cloud.dialogflowcx_v3.types.agent import GetAgentRequest +from google.cloud.dialogflowcx_v3.types.agent import ListAgentsRequest +from google.cloud.dialogflowcx_v3.types.agent import ListAgentsResponse +from google.cloud.dialogflowcx_v3.types.agent import RestoreAgentRequest +from google.cloud.dialogflowcx_v3.types.agent import SpeechToTextSettings +from google.cloud.dialogflowcx_v3.types.agent import UpdateAgentRequest +from google.cloud.dialogflowcx_v3.types.audio_config import AudioEncoding +from google.cloud.dialogflowcx_v3.types.audio_config import InputAudioConfig +from google.cloud.dialogflowcx_v3.types.audio_config import OutputAudioConfig +from google.cloud.dialogflowcx_v3.types.audio_config import OutputAudioEncoding +from google.cloud.dialogflowcx_v3.types.audio_config import SpeechModelVariant +from google.cloud.dialogflowcx_v3.types.audio_config import SpeechWordInfo +from google.cloud.dialogflowcx_v3.types.audio_config import SsmlVoiceGender +from google.cloud.dialogflowcx_v3.types.audio_config import SynthesizeSpeechConfig +from google.cloud.dialogflowcx_v3.types.audio_config import VoiceSelectionParams +from google.cloud.dialogflowcx_v3.types.entity_type import CreateEntityTypeRequest +from google.cloud.dialogflowcx_v3.types.entity_type import DeleteEntityTypeRequest +from google.cloud.dialogflowcx_v3.types.entity_type import EntityType +from google.cloud.dialogflowcx_v3.types.entity_type import GetEntityTypeRequest +from google.cloud.dialogflowcx_v3.types.entity_type import ListEntityTypesRequest +from google.cloud.dialogflowcx_v3.types.entity_type import ListEntityTypesResponse +from google.cloud.dialogflowcx_v3.types.entity_type import UpdateEntityTypeRequest +from google.cloud.dialogflowcx_v3.types.environment import CreateEnvironmentRequest +from google.cloud.dialogflowcx_v3.types.environment import DeleteEnvironmentRequest +from google.cloud.dialogflowcx_v3.types.environment import Environment +from google.cloud.dialogflowcx_v3.types.environment import GetEnvironmentRequest +from google.cloud.dialogflowcx_v3.types.environment import ListEnvironmentsRequest +from google.cloud.dialogflowcx_v3.types.environment import ListEnvironmentsResponse +from google.cloud.dialogflowcx_v3.types.environment import ( LookupEnvironmentHistoryRequest, ) -from google.cloud.dialogflowcx_v3beta1.types.environment import ( +from google.cloud.dialogflowcx_v3.types.environment import ( LookupEnvironmentHistoryResponse, ) -from google.cloud.dialogflowcx_v3beta1.types.environment import UpdateEnvironmentRequest -from google.cloud.dialogflowcx_v3beta1.types.flow import CreateFlowRequest -from google.cloud.dialogflowcx_v3beta1.types.flow import DeleteFlowRequest -from google.cloud.dialogflowcx_v3beta1.types.flow import Flow -from google.cloud.dialogflowcx_v3beta1.types.flow import GetFlowRequest -from google.cloud.dialogflowcx_v3beta1.types.flow import ListFlowsRequest -from google.cloud.dialogflowcx_v3beta1.types.flow import ListFlowsResponse -from google.cloud.dialogflowcx_v3beta1.types.flow import NluSettings -from google.cloud.dialogflowcx_v3beta1.types.flow import TrainFlowRequest -from google.cloud.dialogflowcx_v3beta1.types.flow import UpdateFlowRequest -from google.cloud.dialogflowcx_v3beta1.types.fulfillment import Fulfillment -from google.cloud.dialogflowcx_v3beta1.types.intent import CreateIntentRequest -from google.cloud.dialogflowcx_v3beta1.types.intent import DeleteIntentRequest -from google.cloud.dialogflowcx_v3beta1.types.intent import GetIntentRequest -from google.cloud.dialogflowcx_v3beta1.types.intent import Intent -from google.cloud.dialogflowcx_v3beta1.types.intent import IntentView -from google.cloud.dialogflowcx_v3beta1.types.intent import ListIntentsRequest -from google.cloud.dialogflowcx_v3beta1.types.intent import ListIntentsResponse -from google.cloud.dialogflowcx_v3beta1.types.intent import UpdateIntentRequest -from google.cloud.dialogflowcx_v3beta1.types.page import CreatePageRequest -from google.cloud.dialogflowcx_v3beta1.types.page import DeletePageRequest -from google.cloud.dialogflowcx_v3beta1.types.page import EventHandler -from google.cloud.dialogflowcx_v3beta1.types.page import Form -from google.cloud.dialogflowcx_v3beta1.types.page import GetPageRequest -from google.cloud.dialogflowcx_v3beta1.types.page import ListPagesRequest -from google.cloud.dialogflowcx_v3beta1.types.page import ListPagesResponse -from google.cloud.dialogflowcx_v3beta1.types.page import Page -from google.cloud.dialogflowcx_v3beta1.types.page import TransitionRoute -from google.cloud.dialogflowcx_v3beta1.types.page import UpdatePageRequest -from google.cloud.dialogflowcx_v3beta1.types.response_message import ResponseMessage -from google.cloud.dialogflowcx_v3beta1.types.session import AudioInput -from google.cloud.dialogflowcx_v3beta1.types.session import DetectIntentRequest -from google.cloud.dialogflowcx_v3beta1.types.session import DetectIntentResponse -from google.cloud.dialogflowcx_v3beta1.types.session import FulfillIntentRequest -from google.cloud.dialogflowcx_v3beta1.types.session import FulfillIntentResponse -from google.cloud.dialogflowcx_v3beta1.types.session import IntentInput -from google.cloud.dialogflowcx_v3beta1.types.session import Match -from google.cloud.dialogflowcx_v3beta1.types.session import MatchIntentRequest -from google.cloud.dialogflowcx_v3beta1.types.session import MatchIntentResponse -from google.cloud.dialogflowcx_v3beta1.types.session import QueryInput -from google.cloud.dialogflowcx_v3beta1.types.session import QueryParameters -from google.cloud.dialogflowcx_v3beta1.types.session import QueryResult -from google.cloud.dialogflowcx_v3beta1.types.session import StreamingDetectIntentRequest -from google.cloud.dialogflowcx_v3beta1.types.session import ( - StreamingDetectIntentResponse, -) -from google.cloud.dialogflowcx_v3beta1.types.session import StreamingRecognitionResult -from google.cloud.dialogflowcx_v3beta1.types.session import TextInput -from google.cloud.dialogflowcx_v3beta1.types.session_entity_type import ( +from google.cloud.dialogflowcx_v3.types.environment import UpdateEnvironmentRequest +from google.cloud.dialogflowcx_v3.types.flow import CreateFlowRequest +from google.cloud.dialogflowcx_v3.types.flow import DeleteFlowRequest +from google.cloud.dialogflowcx_v3.types.flow import Flow +from google.cloud.dialogflowcx_v3.types.flow import GetFlowRequest +from google.cloud.dialogflowcx_v3.types.flow import ListFlowsRequest +from google.cloud.dialogflowcx_v3.types.flow import ListFlowsResponse +from google.cloud.dialogflowcx_v3.types.flow import NluSettings +from google.cloud.dialogflowcx_v3.types.flow import TrainFlowRequest +from google.cloud.dialogflowcx_v3.types.flow import UpdateFlowRequest +from google.cloud.dialogflowcx_v3.types.fulfillment import Fulfillment +from google.cloud.dialogflowcx_v3.types.intent import CreateIntentRequest +from google.cloud.dialogflowcx_v3.types.intent import DeleteIntentRequest +from google.cloud.dialogflowcx_v3.types.intent import GetIntentRequest +from google.cloud.dialogflowcx_v3.types.intent import Intent +from google.cloud.dialogflowcx_v3.types.intent import IntentView +from google.cloud.dialogflowcx_v3.types.intent import ListIntentsRequest +from google.cloud.dialogflowcx_v3.types.intent import ListIntentsResponse +from google.cloud.dialogflowcx_v3.types.intent import UpdateIntentRequest +from google.cloud.dialogflowcx_v3.types.page import CreatePageRequest +from google.cloud.dialogflowcx_v3.types.page import DeletePageRequest +from google.cloud.dialogflowcx_v3.types.page import EventHandler +from google.cloud.dialogflowcx_v3.types.page import Form +from google.cloud.dialogflowcx_v3.types.page import GetPageRequest +from google.cloud.dialogflowcx_v3.types.page import ListPagesRequest +from google.cloud.dialogflowcx_v3.types.page import ListPagesResponse +from google.cloud.dialogflowcx_v3.types.page import Page +from google.cloud.dialogflowcx_v3.types.page import TransitionRoute +from google.cloud.dialogflowcx_v3.types.page import UpdatePageRequest +from google.cloud.dialogflowcx_v3.types.response_message import ResponseMessage +from google.cloud.dialogflowcx_v3.types.security_settings import ( + CreateSecuritySettingsRequest, +) +from google.cloud.dialogflowcx_v3.types.security_settings import ( + DeleteSecuritySettingsRequest, +) +from google.cloud.dialogflowcx_v3.types.security_settings import ( + GetSecuritySettingsRequest, +) +from google.cloud.dialogflowcx_v3.types.security_settings import ( + ListSecuritySettingsRequest, +) +from google.cloud.dialogflowcx_v3.types.security_settings import ( + ListSecuritySettingsResponse, +) +from google.cloud.dialogflowcx_v3.types.security_settings import SecuritySettings +from google.cloud.dialogflowcx_v3.types.security_settings import ( + UpdateSecuritySettingsRequest, +) +from google.cloud.dialogflowcx_v3.types.session import AudioInput +from google.cloud.dialogflowcx_v3.types.session import DetectIntentRequest +from google.cloud.dialogflowcx_v3.types.session import DetectIntentResponse +from google.cloud.dialogflowcx_v3.types.session import DtmfInput +from google.cloud.dialogflowcx_v3.types.session import EventInput +from google.cloud.dialogflowcx_v3.types.session import FulfillIntentRequest +from google.cloud.dialogflowcx_v3.types.session import FulfillIntentResponse +from google.cloud.dialogflowcx_v3.types.session import IntentInput +from google.cloud.dialogflowcx_v3.types.session import Match +from google.cloud.dialogflowcx_v3.types.session import MatchIntentRequest +from google.cloud.dialogflowcx_v3.types.session import MatchIntentResponse +from google.cloud.dialogflowcx_v3.types.session import QueryInput +from google.cloud.dialogflowcx_v3.types.session import QueryParameters +from google.cloud.dialogflowcx_v3.types.session import QueryResult +from google.cloud.dialogflowcx_v3.types.session import SentimentAnalysisResult +from google.cloud.dialogflowcx_v3.types.session import StreamingDetectIntentRequest +from google.cloud.dialogflowcx_v3.types.session import StreamingDetectIntentResponse +from google.cloud.dialogflowcx_v3.types.session import StreamingRecognitionResult +from google.cloud.dialogflowcx_v3.types.session import TextInput +from google.cloud.dialogflowcx_v3.types.session_entity_type import ( CreateSessionEntityTypeRequest, ) -from google.cloud.dialogflowcx_v3beta1.types.session_entity_type import ( +from google.cloud.dialogflowcx_v3.types.session_entity_type import ( DeleteSessionEntityTypeRequest, ) -from google.cloud.dialogflowcx_v3beta1.types.session_entity_type import ( +from google.cloud.dialogflowcx_v3.types.session_entity_type import ( GetSessionEntityTypeRequest, ) -from google.cloud.dialogflowcx_v3beta1.types.session_entity_type import ( +from google.cloud.dialogflowcx_v3.types.session_entity_type import ( ListSessionEntityTypesRequest, ) -from google.cloud.dialogflowcx_v3beta1.types.session_entity_type import ( +from google.cloud.dialogflowcx_v3.types.session_entity_type import ( ListSessionEntityTypesResponse, ) -from google.cloud.dialogflowcx_v3beta1.types.session_entity_type import ( - SessionEntityType, -) -from google.cloud.dialogflowcx_v3beta1.types.session_entity_type import ( +from google.cloud.dialogflowcx_v3.types.session_entity_type import SessionEntityType +from google.cloud.dialogflowcx_v3.types.session_entity_type import ( UpdateSessionEntityTypeRequest, ) -from google.cloud.dialogflowcx_v3beta1.types.transition_route_group import ( +from google.cloud.dialogflowcx_v3.types.transition_route_group import ( CreateTransitionRouteGroupRequest, ) -from google.cloud.dialogflowcx_v3beta1.types.transition_route_group import ( +from google.cloud.dialogflowcx_v3.types.transition_route_group import ( DeleteTransitionRouteGroupRequest, ) -from google.cloud.dialogflowcx_v3beta1.types.transition_route_group import ( +from google.cloud.dialogflowcx_v3.types.transition_route_group import ( GetTransitionRouteGroupRequest, ) -from google.cloud.dialogflowcx_v3beta1.types.transition_route_group import ( +from google.cloud.dialogflowcx_v3.types.transition_route_group import ( ListTransitionRouteGroupsRequest, ) -from google.cloud.dialogflowcx_v3beta1.types.transition_route_group import ( +from google.cloud.dialogflowcx_v3.types.transition_route_group import ( ListTransitionRouteGroupsResponse, ) -from google.cloud.dialogflowcx_v3beta1.types.transition_route_group import ( +from google.cloud.dialogflowcx_v3.types.transition_route_group import ( TransitionRouteGroup, ) -from google.cloud.dialogflowcx_v3beta1.types.transition_route_group import ( +from google.cloud.dialogflowcx_v3.types.transition_route_group import ( UpdateTransitionRouteGroupRequest, ) -from google.cloud.dialogflowcx_v3beta1.types.version import ( - CreateVersionOperationMetadata, -) -from google.cloud.dialogflowcx_v3beta1.types.version import CreateVersionRequest -from google.cloud.dialogflowcx_v3beta1.types.version import DeleteVersionRequest -from google.cloud.dialogflowcx_v3beta1.types.version import GetVersionRequest -from google.cloud.dialogflowcx_v3beta1.types.version import ListVersionsRequest -from google.cloud.dialogflowcx_v3beta1.types.version import ListVersionsResponse -from google.cloud.dialogflowcx_v3beta1.types.version import LoadVersionRequest -from google.cloud.dialogflowcx_v3beta1.types.version import UpdateVersionRequest -from google.cloud.dialogflowcx_v3beta1.types.version import Version -from google.cloud.dialogflowcx_v3beta1.types.webhook import CreateWebhookRequest -from google.cloud.dialogflowcx_v3beta1.types.webhook import DeleteWebhookRequest -from google.cloud.dialogflowcx_v3beta1.types.webhook import GetWebhookRequest -from google.cloud.dialogflowcx_v3beta1.types.webhook import ListWebhooksRequest -from google.cloud.dialogflowcx_v3beta1.types.webhook import ListWebhooksResponse -from google.cloud.dialogflowcx_v3beta1.types.webhook import PageInfo -from google.cloud.dialogflowcx_v3beta1.types.webhook import SessionInfo -from google.cloud.dialogflowcx_v3beta1.types.webhook import UpdateWebhookRequest -from google.cloud.dialogflowcx_v3beta1.types.webhook import Webhook -from google.cloud.dialogflowcx_v3beta1.types.webhook import WebhookRequest -from google.cloud.dialogflowcx_v3beta1.types.webhook import WebhookResponse +from google.cloud.dialogflowcx_v3.types.version import CreateVersionOperationMetadata +from google.cloud.dialogflowcx_v3.types.version import CreateVersionRequest +from google.cloud.dialogflowcx_v3.types.version import DeleteVersionRequest +from google.cloud.dialogflowcx_v3.types.version import GetVersionRequest +from google.cloud.dialogflowcx_v3.types.version import ListVersionsRequest +from google.cloud.dialogflowcx_v3.types.version import ListVersionsResponse +from google.cloud.dialogflowcx_v3.types.version import LoadVersionRequest +from google.cloud.dialogflowcx_v3.types.version import UpdateVersionRequest +from google.cloud.dialogflowcx_v3.types.version import Version +from google.cloud.dialogflowcx_v3.types.webhook import CreateWebhookRequest +from google.cloud.dialogflowcx_v3.types.webhook import DeleteWebhookRequest +from google.cloud.dialogflowcx_v3.types.webhook import GetWebhookRequest +from google.cloud.dialogflowcx_v3.types.webhook import ListWebhooksRequest +from google.cloud.dialogflowcx_v3.types.webhook import ListWebhooksResponse +from google.cloud.dialogflowcx_v3.types.webhook import PageInfo +from google.cloud.dialogflowcx_v3.types.webhook import SessionInfo +from google.cloud.dialogflowcx_v3.types.webhook import UpdateWebhookRequest +from google.cloud.dialogflowcx_v3.types.webhook import Webhook +from google.cloud.dialogflowcx_v3.types.webhook import WebhookRequest +from google.cloud.dialogflowcx_v3.types.webhook import WebhookResponse __all__ = ( "Agent", @@ -231,6 +243,7 @@ "CreateFlowRequest", "CreateIntentRequest", "CreatePageRequest", + "CreateSecuritySettingsRequest", "CreateSessionEntityTypeRequest", "CreateTransitionRouteGroupRequest", "CreateVersionOperationMetadata", @@ -242,12 +255,14 @@ "DeleteFlowRequest", "DeleteIntentRequest", "DeletePageRequest", + "DeleteSecuritySettingsRequest", "DeleteSessionEntityTypeRequest", "DeleteTransitionRouteGroupRequest", "DeleteVersionRequest", "DeleteWebhookRequest", "DetectIntentRequest", "DetectIntentResponse", + "DtmfInput", "EntityType", "EntityTypesAsyncClient", "EntityTypesClient", @@ -255,6 +270,7 @@ "EnvironmentsAsyncClient", "EnvironmentsClient", "EventHandler", + "EventInput", "ExportAgentRequest", "ExportAgentResponse", "Flow", @@ -270,6 +286,7 @@ "GetFlowRequest", "GetIntentRequest", "GetPageRequest", + "GetSecuritySettingsRequest", "GetSessionEntityTypeRequest", "GetTransitionRouteGroupRequest", "GetVersionRequest", @@ -292,6 +309,8 @@ "ListIntentsResponse", "ListPagesRequest", "ListPagesResponse", + "ListSecuritySettingsRequest", + "ListSecuritySettingsResponse", "ListSessionEntityTypesRequest", "ListSessionEntityTypesResponse", "ListTransitionRouteGroupsRequest", @@ -318,6 +337,10 @@ "QueryResult", "ResponseMessage", "RestoreAgentRequest", + "SecuritySettings", + "SecuritySettingsServiceAsyncClient", + "SecuritySettingsServiceClient", + "SentimentAnalysisResult", "SessionEntityType", "SessionEntityTypesAsyncClient", "SessionEntityTypesClient", @@ -344,6 +367,7 @@ "UpdateFlowRequest", "UpdateIntentRequest", "UpdatePageRequest", + "UpdateSecuritySettingsRequest", "UpdateSessionEntityTypeRequest", "UpdateTransitionRouteGroupRequest", "UpdateVersionRequest", diff --git a/google/cloud/dialogflowcx_v3/__init__.py b/google/cloud/dialogflowcx_v3/__init__.py new file mode 100644 index 00000000..791c87bc --- /dev/null +++ b/google/cloud/dialogflowcx_v3/__init__.py @@ -0,0 +1,295 @@ +# -*- 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.agents import AgentsClient +from .services.entity_types import EntityTypesClient +from .services.environments import EnvironmentsClient +from .services.flows import FlowsClient +from .services.intents import IntentsClient +from .services.pages import PagesClient +from .services.security_settings_service import SecuritySettingsServiceClient +from .services.session_entity_types import SessionEntityTypesClient +from .services.sessions import SessionsClient +from .services.transition_route_groups import TransitionRouteGroupsClient +from .services.versions import VersionsClient +from .services.webhooks import WebhooksClient +from .types.agent import Agent +from .types.agent import CreateAgentRequest +from .types.agent import DeleteAgentRequest +from .types.agent import ExportAgentRequest +from .types.agent import ExportAgentResponse +from .types.agent import GetAgentRequest +from .types.agent import ListAgentsRequest +from .types.agent import ListAgentsResponse +from .types.agent import RestoreAgentRequest +from .types.agent import SpeechToTextSettings +from .types.agent import UpdateAgentRequest +from .types.audio_config import AudioEncoding +from .types.audio_config import InputAudioConfig +from .types.audio_config import OutputAudioConfig +from .types.audio_config import OutputAudioEncoding +from .types.audio_config import SpeechModelVariant +from .types.audio_config import SpeechWordInfo +from .types.audio_config import SsmlVoiceGender +from .types.audio_config import SynthesizeSpeechConfig +from .types.audio_config import VoiceSelectionParams +from .types.entity_type import CreateEntityTypeRequest +from .types.entity_type import DeleteEntityTypeRequest +from .types.entity_type import EntityType +from .types.entity_type import GetEntityTypeRequest +from .types.entity_type import ListEntityTypesRequest +from .types.entity_type import ListEntityTypesResponse +from .types.entity_type import UpdateEntityTypeRequest +from .types.environment import CreateEnvironmentRequest +from .types.environment import DeleteEnvironmentRequest +from .types.environment import Environment +from .types.environment import GetEnvironmentRequest +from .types.environment import ListEnvironmentsRequest +from .types.environment import ListEnvironmentsResponse +from .types.environment import LookupEnvironmentHistoryRequest +from .types.environment import LookupEnvironmentHistoryResponse +from .types.environment import UpdateEnvironmentRequest +from .types.flow import CreateFlowRequest +from .types.flow import DeleteFlowRequest +from .types.flow import Flow +from .types.flow import GetFlowRequest +from .types.flow import ListFlowsRequest +from .types.flow import ListFlowsResponse +from .types.flow import NluSettings +from .types.flow import TrainFlowRequest +from .types.flow import UpdateFlowRequest +from .types.fulfillment import Fulfillment +from .types.intent import CreateIntentRequest +from .types.intent import DeleteIntentRequest +from .types.intent import GetIntentRequest +from .types.intent import Intent +from .types.intent import IntentView +from .types.intent import ListIntentsRequest +from .types.intent import ListIntentsResponse +from .types.intent import UpdateIntentRequest +from .types.page import CreatePageRequest +from .types.page import DeletePageRequest +from .types.page import EventHandler +from .types.page import Form +from .types.page import GetPageRequest +from .types.page import ListPagesRequest +from .types.page import ListPagesResponse +from .types.page import Page +from .types.page import TransitionRoute +from .types.page import UpdatePageRequest +from .types.response_message import ResponseMessage +from .types.security_settings import CreateSecuritySettingsRequest +from .types.security_settings import DeleteSecuritySettingsRequest +from .types.security_settings import GetSecuritySettingsRequest +from .types.security_settings import ListSecuritySettingsRequest +from .types.security_settings import ListSecuritySettingsResponse +from .types.security_settings import SecuritySettings +from .types.security_settings import UpdateSecuritySettingsRequest +from .types.session import AudioInput +from .types.session import DetectIntentRequest +from .types.session import DetectIntentResponse +from .types.session import DtmfInput +from .types.session import EventInput +from .types.session import FulfillIntentRequest +from .types.session import FulfillIntentResponse +from .types.session import IntentInput +from .types.session import Match +from .types.session import MatchIntentRequest +from .types.session import MatchIntentResponse +from .types.session import QueryInput +from .types.session import QueryParameters +from .types.session import QueryResult +from .types.session import SentimentAnalysisResult +from .types.session import StreamingDetectIntentRequest +from .types.session import StreamingDetectIntentResponse +from .types.session import StreamingRecognitionResult +from .types.session import TextInput +from .types.session_entity_type import CreateSessionEntityTypeRequest +from .types.session_entity_type import DeleteSessionEntityTypeRequest +from .types.session_entity_type import GetSessionEntityTypeRequest +from .types.session_entity_type import ListSessionEntityTypesRequest +from .types.session_entity_type import ListSessionEntityTypesResponse +from .types.session_entity_type import SessionEntityType +from .types.session_entity_type import UpdateSessionEntityTypeRequest +from .types.transition_route_group import CreateTransitionRouteGroupRequest +from .types.transition_route_group import DeleteTransitionRouteGroupRequest +from .types.transition_route_group import GetTransitionRouteGroupRequest +from .types.transition_route_group import ListTransitionRouteGroupsRequest +from .types.transition_route_group import ListTransitionRouteGroupsResponse +from .types.transition_route_group import TransitionRouteGroup +from .types.transition_route_group import UpdateTransitionRouteGroupRequest +from .types.version import CreateVersionOperationMetadata +from .types.version import CreateVersionRequest +from .types.version import DeleteVersionRequest +from .types.version import GetVersionRequest +from .types.version import ListVersionsRequest +from .types.version import ListVersionsResponse +from .types.version import LoadVersionRequest +from .types.version import UpdateVersionRequest +from .types.version import Version +from .types.webhook import CreateWebhookRequest +from .types.webhook import DeleteWebhookRequest +from .types.webhook import GetWebhookRequest +from .types.webhook import ListWebhooksRequest +from .types.webhook import ListWebhooksResponse +from .types.webhook import PageInfo +from .types.webhook import SessionInfo +from .types.webhook import UpdateWebhookRequest +from .types.webhook import Webhook +from .types.webhook import WebhookRequest +from .types.webhook import WebhookResponse + + +__all__ = ( + "Agent", + "AgentsClient", + "AudioEncoding", + "AudioInput", + "CreateAgentRequest", + "CreateEntityTypeRequest", + "CreateEnvironmentRequest", + "CreateFlowRequest", + "CreateIntentRequest", + "CreatePageRequest", + "CreateSecuritySettingsRequest", + "CreateSessionEntityTypeRequest", + "CreateTransitionRouteGroupRequest", + "CreateVersionOperationMetadata", + "CreateVersionRequest", + "CreateWebhookRequest", + "DeleteAgentRequest", + "DeleteEntityTypeRequest", + "DeleteEnvironmentRequest", + "DeleteFlowRequest", + "DeleteIntentRequest", + "DeletePageRequest", + "DeleteSecuritySettingsRequest", + "DeleteSessionEntityTypeRequest", + "DeleteTransitionRouteGroupRequest", + "DeleteVersionRequest", + "DeleteWebhookRequest", + "DetectIntentRequest", + "DetectIntentResponse", + "DtmfInput", + "EntityType", + "EntityTypesClient", + "Environment", + "EnvironmentsClient", + "EventHandler", + "EventInput", + "ExportAgentRequest", + "ExportAgentResponse", + "Flow", + "FlowsClient", + "Form", + "FulfillIntentRequest", + "FulfillIntentResponse", + "Fulfillment", + "GetAgentRequest", + "GetEntityTypeRequest", + "GetEnvironmentRequest", + "GetFlowRequest", + "GetIntentRequest", + "GetPageRequest", + "GetSecuritySettingsRequest", + "GetSessionEntityTypeRequest", + "GetTransitionRouteGroupRequest", + "GetVersionRequest", + "GetWebhookRequest", + "InputAudioConfig", + "Intent", + "IntentInput", + "IntentView", + "IntentsClient", + "ListAgentsRequest", + "ListAgentsResponse", + "ListEntityTypesRequest", + "ListEntityTypesResponse", + "ListEnvironmentsRequest", + "ListEnvironmentsResponse", + "ListFlowsRequest", + "ListFlowsResponse", + "ListIntentsRequest", + "ListIntentsResponse", + "ListPagesRequest", + "ListPagesResponse", + "ListSecuritySettingsRequest", + "ListSecuritySettingsResponse", + "ListSessionEntityTypesRequest", + "ListSessionEntityTypesResponse", + "ListTransitionRouteGroupsRequest", + "ListTransitionRouteGroupsResponse", + "ListVersionsRequest", + "ListVersionsResponse", + "ListWebhooksRequest", + "ListWebhooksResponse", + "LoadVersionRequest", + "LookupEnvironmentHistoryRequest", + "LookupEnvironmentHistoryResponse", + "Match", + "MatchIntentRequest", + "MatchIntentResponse", + "NluSettings", + "OutputAudioConfig", + "OutputAudioEncoding", + "Page", + "PageInfo", + "PagesClient", + "QueryInput", + "QueryParameters", + "QueryResult", + "ResponseMessage", + "RestoreAgentRequest", + "SecuritySettings", + "SecuritySettingsServiceClient", + "SentimentAnalysisResult", + "SessionEntityType", + "SessionEntityTypesClient", + "SessionInfo", + "SessionsClient", + "SpeechModelVariant", + "SpeechToTextSettings", + "SpeechWordInfo", + "SsmlVoiceGender", + "StreamingDetectIntentRequest", + "StreamingDetectIntentResponse", + "StreamingRecognitionResult", + "SynthesizeSpeechConfig", + "TextInput", + "TrainFlowRequest", + "TransitionRoute", + "TransitionRouteGroup", + "TransitionRouteGroupsClient", + "UpdateAgentRequest", + "UpdateEntityTypeRequest", + "UpdateEnvironmentRequest", + "UpdateFlowRequest", + "UpdateIntentRequest", + "UpdatePageRequest", + "UpdateSecuritySettingsRequest", + "UpdateSessionEntityTypeRequest", + "UpdateTransitionRouteGroupRequest", + "UpdateVersionRequest", + "UpdateWebhookRequest", + "Version", + "VersionsClient", + "VoiceSelectionParams", + "Webhook", + "WebhookRequest", + "WebhookResponse", + "WebhooksClient", +) diff --git a/google/cloud/dialogflowcx_v3/py.typed b/google/cloud/dialogflowcx_v3/py.typed new file mode 100644 index 00000000..60fdca49 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/py.typed @@ -0,0 +1,2 @@ +# Marker file for PEP 561. +# The google-cloud-dialogflowcx package uses inline types. diff --git a/google/cloud/dialogflowcx_v3/services/__init__.py b/google/cloud/dialogflowcx_v3/services/__init__.py new file mode 100644 index 00000000..42ffdf2b --- /dev/null +++ b/google/cloud/dialogflowcx_v3/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/dialogflowcx_v3beta1/types/gcs.py b/google/cloud/dialogflowcx_v3/services/agents/__init__.py similarity index 81% rename from google/cloud/dialogflowcx_v3beta1/types/gcs.py rename to google/cloud/dialogflowcx_v3/services/agents/__init__.py index 32c9622b..4bcbd7ac 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/gcs.py +++ b/google/cloud/dialogflowcx_v3/services/agents/__init__.py @@ -15,8 +15,10 @@ # limitations under the License. # +from .client import AgentsClient +from .async_client import AgentsAsyncClient -__protobuf__ = proto.module(package="google.cloud.dialogflow.cx.v3beta1", manifest={},) - - -__all__ = tuple(sorted(__protobuf__.manifest)) +__all__ = ( + "AgentsClient", + "AgentsAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/agents/async_client.py b/google/cloud/dialogflowcx_v3/services/agents/async_client.py new file mode 100644 index 00000000..49935e19 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/agents/async_client.py @@ -0,0 +1,707 @@ +# -*- 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.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.dialogflowcx_v3.services.agents import pagers +from google.cloud.dialogflowcx_v3.types import agent +from google.cloud.dialogflowcx_v3.types import agent as gcdc_agent +from google.protobuf import empty_pb2 as empty # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore + +from .transports.base import AgentsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import AgentsGrpcAsyncIOTransport +from .client import AgentsClient + + +class AgentsAsyncClient: + """Service for managing [Agents][google.cloud.dialogflow.cx.v3.Agent].""" + + _client: AgentsClient + + DEFAULT_ENDPOINT = AgentsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = AgentsClient.DEFAULT_MTLS_ENDPOINT + + agent_path = staticmethod(AgentsClient.agent_path) + parse_agent_path = staticmethod(AgentsClient.parse_agent_path) + flow_path = staticmethod(AgentsClient.flow_path) + parse_flow_path = staticmethod(AgentsClient.parse_flow_path) + + common_billing_account_path = staticmethod(AgentsClient.common_billing_account_path) + parse_common_billing_account_path = staticmethod( + AgentsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(AgentsClient.common_folder_path) + parse_common_folder_path = staticmethod(AgentsClient.parse_common_folder_path) + + common_organization_path = staticmethod(AgentsClient.common_organization_path) + parse_common_organization_path = staticmethod( + AgentsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(AgentsClient.common_project_path) + parse_common_project_path = staticmethod(AgentsClient.parse_common_project_path) + + common_location_path = staticmethod(AgentsClient.common_location_path) + parse_common_location_path = staticmethod(AgentsClient.parse_common_location_path) + + from_service_account_file = AgentsClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> AgentsTransport: + """Return the transport used by the client instance. + + Returns: + AgentsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(AgentsClient).get_transport_class, type(AgentsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, AgentsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the agents 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, ~.AgentsTransport]): 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 = AgentsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_agents( + self, + request: agent.ListAgentsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListAgentsAsyncPager: + r"""Returns the list of all agents in the specified + location. + + Args: + request (:class:`~.agent.ListAgentsRequest`): + The request object. The request message for + [Agents.ListAgents][google.cloud.dialogflow.cx.v3.Agents.ListAgents]. + parent (:class:`str`): + Required. The location to list all agents for. Format: + ``projects//locations/``. + 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: + ~.pagers.ListAgentsAsyncPager: + The response message for + [Agents.ListAgents][google.cloud.dialogflow.cx.v3.Agents.ListAgents]. + + 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 = agent.ListAgentsRequest(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_agents, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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.ListAgentsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_agent( + self, + request: agent.GetAgentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> agent.Agent: + r"""Retrieves the specified agent. + + Args: + request (:class:`~.agent.GetAgentRequest`): + The request object. The request message for + [Agents.GetAgent][google.cloud.dialogflow.cx.v3.Agents.GetAgent]. + name (:class:`str`): + Required. The name of the agent. Format: + ``projects//locations//agents/``. + 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: + ~.agent.Agent: + Agents are best described as Natural Language + Understanding (NLU) modules that transform user requests + into actionable data. You can include agents in your + app, product, or service to determine user intent and + respond to the user in a natural way. + + After you create an agent, you can add + [Intents][google.cloud.dialogflow.cx.v3.Intent], [Entity + Types][google.cloud.dialogflow.cx.v3.EntityType], + [Flows][google.cloud.dialogflow.cx.v3.Flow], + [Fulfillments][google.cloud.dialogflow.cx.v3.Fulfillment], + [Webhooks][google.cloud.dialogflow.cx.v3.Webhook], and + so on to manage the conversation flows.. + + """ + # 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 = agent.GetAgentRequest(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_agent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_agent( + self, + request: gcdc_agent.CreateAgentRequest = None, + *, + parent: str = None, + agent: gcdc_agent.Agent = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_agent.Agent: + r"""Creates an agent in the specified location. + + Args: + request (:class:`~.gcdc_agent.CreateAgentRequest`): + The request object. The request message for + [Agents.CreateAgent][google.cloud.dialogflow.cx.v3.Agents.CreateAgent]. + parent (:class:`str`): + Required. The location to create a agent for. Format: + ``projects//locations/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + agent (:class:`~.gcdc_agent.Agent`): + Required. The agent to create. + This corresponds to the ``agent`` 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: + ~.gcdc_agent.Agent: + Agents are best described as Natural Language + Understanding (NLU) modules that transform user requests + into actionable data. You can include agents in your + app, product, or service to determine user intent and + respond to the user in a natural way. + + After you create an agent, you can add + [Intents][google.cloud.dialogflow.cx.v3.Intent], [Entity + Types][google.cloud.dialogflow.cx.v3.EntityType], + [Flows][google.cloud.dialogflow.cx.v3.Flow], + [Fulfillments][google.cloud.dialogflow.cx.v3.Fulfillment], + [Webhooks][google.cloud.dialogflow.cx.v3.Webhook], and + so on to manage the conversation flows.. + + """ + # 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, agent]) + 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 = gcdc_agent.CreateAgentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if agent is not None: + request.agent = agent + + # 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_agent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_agent( + self, + request: gcdc_agent.UpdateAgentRequest = None, + *, + agent: gcdc_agent.Agent = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_agent.Agent: + r"""Updates the specified agent. + + Args: + request (:class:`~.gcdc_agent.UpdateAgentRequest`): + The request object. The request message for + [Agents.UpdateAgent][google.cloud.dialogflow.cx.v3.Agents.UpdateAgent]. + agent (:class:`~.gcdc_agent.Agent`): + Required. The agent to update. + This corresponds to the ``agent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. If the mask is not present, all + fields will be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_agent.Agent: + Agents are best described as Natural Language + Understanding (NLU) modules that transform user requests + into actionable data. You can include agents in your + app, product, or service to determine user intent and + respond to the user in a natural way. + + After you create an agent, you can add + [Intents][google.cloud.dialogflow.cx.v3.Intent], [Entity + Types][google.cloud.dialogflow.cx.v3.EntityType], + [Flows][google.cloud.dialogflow.cx.v3.Flow], + [Fulfillments][google.cloud.dialogflow.cx.v3.Fulfillment], + [Webhooks][google.cloud.dialogflow.cx.v3.Webhook], and + so on to manage the conversation flows.. + + """ + # 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([agent, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = gcdc_agent.UpdateAgentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if agent is not None: + request.agent = agent + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_agent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("agent.name", request.agent.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_agent( + self, + request: agent.DeleteAgentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified agent. + + Args: + request (:class:`~.agent.DeleteAgentRequest`): + The request object. The request message for + [Agents.DeleteAgent][google.cloud.dialogflow.cx.v3.Agents.DeleteAgent]. + name (:class:`str`): + Required. The name of the agent to delete. Format: + ``projects//locations//agents/``. + 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 = agent.DeleteAgentRequest(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_agent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + await rpc( + request, retry=retry, timeout=timeout, metadata=metadata, + ) + + async def export_agent( + self, + request: agent.ExportAgentRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Exports the specified agent to a binary file. + + Args: + request (:class:`~.agent.ExportAgentRequest`): + The request object. The request message for + [Agents.ExportAgent][google.cloud.dialogflow.cx.v3.Agents.ExportAgent]. + + 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: + ~.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.agent.ExportAgentResponse``: The response + message for + [Agents.ExportAgent][google.cloud.dialogflow.cx.v3.Agents.ExportAgent]. + + """ + # Create or coerce a protobuf request object. + + request = agent.ExportAgentRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.export_agent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + agent.ExportAgentResponse, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + async def restore_agent( + self, + request: agent.RestoreAgentRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Restores the specified agent from a binary file. + Replaces the current agent with a new one. Note that all + existing resources in agent (e.g. intents, entity types, + flows) will be removed. + + Args: + request (:class:`~.agent.RestoreAgentRequest`): + The request object. The request message for + [Agents.RestoreAgent][google.cloud.dialogflow.cx.v3.Agents.RestoreAgent]. + + 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: + ~.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.empty.Empty``: A generic empty message that + you can re-use to avoid defining duplicated empty + messages in your APIs. A typical example is to use it as + the request or the response type of an API method. For + instance: + + :: + + service Foo { + rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); + } + + The JSON representation for ``Empty`` is empty JSON + object ``{}``. + + """ + # Create or coerce a protobuf request object. + + request = agent.RestoreAgentRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.restore_agent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + empty.Empty, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("AgentsAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/agents/client.py b/google/cloud/dialogflowcx_v3/services/agents/client.py new file mode 100644 index 00000000..67b8d2e7 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/agents/client.py @@ -0,0 +1,927 @@ +# -*- 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.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.dialogflowcx_v3.services.agents import pagers +from google.cloud.dialogflowcx_v3.types import agent +from google.cloud.dialogflowcx_v3.types import agent as gcdc_agent +from google.protobuf import empty_pb2 as empty # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore + +from .transports.base import AgentsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import AgentsGrpcTransport +from .transports.grpc_asyncio import AgentsGrpcAsyncIOTransport + + +class AgentsClientMeta(type): + """Metaclass for the Agents 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[AgentsTransport]] + _transport_registry["grpc"] = AgentsGrpcTransport + _transport_registry["grpc_asyncio"] = AgentsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[AgentsTransport]: + """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 AgentsClient(metaclass=AgentsClientMeta): + """Service for managing [Agents][google.cloud.dialogflow.cx.v3.Agent].""" + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> AgentsTransport: + """Return the transport used by the client instance. + + Returns: + AgentsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def agent_path(project: str, location: str, agent: str,) -> str: + """Return a fully-qualified agent string.""" + return "projects/{project}/locations/{location}/agents/{agent}".format( + project=project, location=location, agent=agent, + ) + + @staticmethod + def parse_agent_path(path: str) -> Dict[str, str]: + """Parse a agent path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def flow_path(project: str, location: str, agent: str, flow: str,) -> str: + """Return a fully-qualified flow string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + + @staticmethod + def parse_flow_path(path: str) -> Dict[str, str]: + """Parse a flow path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?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, AgentsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the agents 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, ~.AgentsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, AgentsTransport): + # transport is a AgentsTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_agents( + self, + request: agent.ListAgentsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListAgentsPager: + r"""Returns the list of all agents in the specified + location. + + Args: + request (:class:`~.agent.ListAgentsRequest`): + The request object. The request message for + [Agents.ListAgents][google.cloud.dialogflow.cx.v3.Agents.ListAgents]. + parent (:class:`str`): + Required. The location to list all agents for. Format: + ``projects//locations/``. + 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: + ~.pagers.ListAgentsPager: + The response message for + [Agents.ListAgents][google.cloud.dialogflow.cx.v3.Agents.ListAgents]. + + 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 agent.ListAgentsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, agent.ListAgentsRequest): + request = agent.ListAgentsRequest(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_agents] + + # 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.ListAgentsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_agent( + self, + request: agent.GetAgentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> agent.Agent: + r"""Retrieves the specified agent. + + Args: + request (:class:`~.agent.GetAgentRequest`): + The request object. The request message for + [Agents.GetAgent][google.cloud.dialogflow.cx.v3.Agents.GetAgent]. + name (:class:`str`): + Required. The name of the agent. Format: + ``projects//locations//agents/``. + 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: + ~.agent.Agent: + Agents are best described as Natural Language + Understanding (NLU) modules that transform user requests + into actionable data. You can include agents in your + app, product, or service to determine user intent and + respond to the user in a natural way. + + After you create an agent, you can add + [Intents][google.cloud.dialogflow.cx.v3.Intent], [Entity + Types][google.cloud.dialogflow.cx.v3.EntityType], + [Flows][google.cloud.dialogflow.cx.v3.Flow], + [Fulfillments][google.cloud.dialogflow.cx.v3.Fulfillment], + [Webhooks][google.cloud.dialogflow.cx.v3.Webhook], and + so on to manage the conversation flows.. + + """ + # 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 agent.GetAgentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, agent.GetAgentRequest): + request = agent.GetAgentRequest(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_agent] + + # 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_agent( + self, + request: gcdc_agent.CreateAgentRequest = None, + *, + parent: str = None, + agent: gcdc_agent.Agent = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_agent.Agent: + r"""Creates an agent in the specified location. + + Args: + request (:class:`~.gcdc_agent.CreateAgentRequest`): + The request object. The request message for + [Agents.CreateAgent][google.cloud.dialogflow.cx.v3.Agents.CreateAgent]. + parent (:class:`str`): + Required. The location to create a agent for. Format: + ``projects//locations/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + agent (:class:`~.gcdc_agent.Agent`): + Required. The agent to create. + This corresponds to the ``agent`` 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: + ~.gcdc_agent.Agent: + Agents are best described as Natural Language + Understanding (NLU) modules that transform user requests + into actionable data. You can include agents in your + app, product, or service to determine user intent and + respond to the user in a natural way. + + After you create an agent, you can add + [Intents][google.cloud.dialogflow.cx.v3.Intent], [Entity + Types][google.cloud.dialogflow.cx.v3.EntityType], + [Flows][google.cloud.dialogflow.cx.v3.Flow], + [Fulfillments][google.cloud.dialogflow.cx.v3.Fulfillment], + [Webhooks][google.cloud.dialogflow.cx.v3.Webhook], and + so on to manage the conversation flows.. + + """ + # 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, agent]) + 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 gcdc_agent.CreateAgentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_agent.CreateAgentRequest): + request = gcdc_agent.CreateAgentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if agent is not None: + request.agent = agent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_agent] + + # 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_agent( + self, + request: gcdc_agent.UpdateAgentRequest = None, + *, + agent: gcdc_agent.Agent = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_agent.Agent: + r"""Updates the specified agent. + + Args: + request (:class:`~.gcdc_agent.UpdateAgentRequest`): + The request object. The request message for + [Agents.UpdateAgent][google.cloud.dialogflow.cx.v3.Agents.UpdateAgent]. + agent (:class:`~.gcdc_agent.Agent`): + Required. The agent to update. + This corresponds to the ``agent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. If the mask is not present, all + fields will be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_agent.Agent: + Agents are best described as Natural Language + Understanding (NLU) modules that transform user requests + into actionable data. You can include agents in your + app, product, or service to determine user intent and + respond to the user in a natural way. + + After you create an agent, you can add + [Intents][google.cloud.dialogflow.cx.v3.Intent], [Entity + Types][google.cloud.dialogflow.cx.v3.EntityType], + [Flows][google.cloud.dialogflow.cx.v3.Flow], + [Fulfillments][google.cloud.dialogflow.cx.v3.Fulfillment], + [Webhooks][google.cloud.dialogflow.cx.v3.Webhook], and + so on to manage the conversation flows.. + + """ + # 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([agent, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a gcdc_agent.UpdateAgentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_agent.UpdateAgentRequest): + request = gcdc_agent.UpdateAgentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if agent is not None: + request.agent = agent + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_agent] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("agent.name", request.agent.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def delete_agent( + self, + request: agent.DeleteAgentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified agent. + + Args: + request (:class:`~.agent.DeleteAgentRequest`): + The request object. The request message for + [Agents.DeleteAgent][google.cloud.dialogflow.cx.v3.Agents.DeleteAgent]. + name (:class:`str`): + Required. The name of the agent to delete. Format: + ``projects//locations//agents/``. + 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 agent.DeleteAgentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, agent.DeleteAgentRequest): + request = agent.DeleteAgentRequest(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_agent] + + # 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, + ) + + def export_agent( + self, + request: agent.ExportAgentRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Exports the specified agent to a binary file. + + Args: + request (:class:`~.agent.ExportAgentRequest`): + The request object. The request message for + [Agents.ExportAgent][google.cloud.dialogflow.cx.v3.Agents.ExportAgent]. + + 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: + ~.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.agent.ExportAgentResponse``: The response + message for + [Agents.ExportAgent][google.cloud.dialogflow.cx.v3.Agents.ExportAgent]. + + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a agent.ExportAgentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, agent.ExportAgentRequest): + request = agent.ExportAgentRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.export_agent] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + agent.ExportAgentResponse, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + def restore_agent( + self, + request: agent.RestoreAgentRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Restores the specified agent from a binary file. + Replaces the current agent with a new one. Note that all + existing resources in agent (e.g. intents, entity types, + flows) will be removed. + + Args: + request (:class:`~.agent.RestoreAgentRequest`): + The request object. The request message for + [Agents.RestoreAgent][google.cloud.dialogflow.cx.v3.Agents.RestoreAgent]. + + 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: + ~.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.empty.Empty``: A generic empty message that + you can re-use to avoid defining duplicated empty + messages in your APIs. A typical example is to use it as + the request or the response type of an API method. For + instance: + + :: + + service Foo { + rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); + } + + The JSON representation for ``Empty`` is empty JSON + object ``{}``. + + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a agent.RestoreAgentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, agent.RestoreAgentRequest): + request = agent.RestoreAgentRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.restore_agent] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + empty.Empty, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("AgentsClient",) diff --git a/google/cloud/dialogflowcx_v3/services/agents/pagers.py b/google/cloud/dialogflowcx_v3/services/agents/pagers.py new file mode 100644 index 00000000..f5fceec5 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/agents/pagers.py @@ -0,0 +1,148 @@ +# -*- 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 + +from google.cloud.dialogflowcx_v3.types import agent + + +class ListAgentsPager: + """A pager for iterating through ``list_agents`` requests. + + This class thinly wraps an initial + :class:`~.agent.ListAgentsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``agents`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListAgents`` requests and continue to iterate + through the ``agents`` field on the + corresponding responses. + + All the usual :class:`~.agent.ListAgentsResponse` + 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[..., agent.ListAgentsResponse], + request: agent.ListAgentsRequest, + response: agent.ListAgentsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.agent.ListAgentsRequest`): + The initial request object. + response (:class:`~.agent.ListAgentsResponse`): + 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 = agent.ListAgentsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[agent.ListAgentsResponse]: + 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[agent.Agent]: + for page in self.pages: + yield from page.agents + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListAgentsAsyncPager: + """A pager for iterating through ``list_agents`` requests. + + This class thinly wraps an initial + :class:`~.agent.ListAgentsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``agents`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListAgents`` requests and continue to iterate + through the ``agents`` field on the + corresponding responses. + + All the usual :class:`~.agent.ListAgentsResponse` + 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[agent.ListAgentsResponse]], + request: agent.ListAgentsRequest, + response: agent.ListAgentsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.agent.ListAgentsRequest`): + The initial request object. + response (:class:`~.agent.ListAgentsResponse`): + 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 = agent.ListAgentsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[agent.ListAgentsResponse]: + 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[agent.Agent]: + async def async_generator(): + async for page in self.pages: + for response in page.agents: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflowcx_v3/services/agents/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/agents/transports/__init__.py new file mode 100644 index 00000000..a87abc01 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/agents/transports/__init__.py @@ -0,0 +1,36 @@ +# -*- 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 AgentsTransport +from .grpc import AgentsGrpcTransport +from .grpc_asyncio import AgentsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[AgentsTransport]] +_transport_registry["grpc"] = AgentsGrpcTransport +_transport_registry["grpc_asyncio"] = AgentsGrpcAsyncIOTransport + + +__all__ = ( + "AgentsTransport", + "AgentsGrpcTransport", + "AgentsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/agents/transports/base.py b/google/cloud/dialogflowcx_v3/services/agents/transports/base.py new file mode 100644 index 00000000..c8f8d077 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/agents/transports/base.py @@ -0,0 +1,210 @@ +# -*- 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.api_core import operations_v1 # type: ignore +from google.auth import credentials # type: ignore + +from google.cloud.dialogflowcx_v3.types import agent +from google.cloud.dialogflowcx_v3.types import agent as gcdc_agent +from google.longrunning import operations_pb2 as operations # type: ignore +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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class AgentsTransport(abc.ABC): + """Abstract transport class for Agents.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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_agents: gapic_v1.method.wrap_method( + self.list_agents, default_timeout=None, client_info=client_info, + ), + self.get_agent: gapic_v1.method.wrap_method( + self.get_agent, default_timeout=None, client_info=client_info, + ), + self.create_agent: gapic_v1.method.wrap_method( + self.create_agent, default_timeout=None, client_info=client_info, + ), + self.update_agent: gapic_v1.method.wrap_method( + self.update_agent, default_timeout=None, client_info=client_info, + ), + self.delete_agent: gapic_v1.method.wrap_method( + self.delete_agent, default_timeout=None, client_info=client_info, + ), + self.export_agent: gapic_v1.method.wrap_method( + self.export_agent, default_timeout=None, client_info=client_info, + ), + self.restore_agent: gapic_v1.method.wrap_method( + self.restore_agent, default_timeout=None, client_info=client_info, + ), + } + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Return the client designed to process long-running operations.""" + raise NotImplementedError() + + @property + def list_agents( + self, + ) -> typing.Callable[ + [agent.ListAgentsRequest], + typing.Union[ + agent.ListAgentsResponse, typing.Awaitable[agent.ListAgentsResponse] + ], + ]: + raise NotImplementedError() + + @property + def get_agent( + self, + ) -> typing.Callable[ + [agent.GetAgentRequest], + typing.Union[agent.Agent, typing.Awaitable[agent.Agent]], + ]: + raise NotImplementedError() + + @property + def create_agent( + self, + ) -> typing.Callable[ + [gcdc_agent.CreateAgentRequest], + typing.Union[gcdc_agent.Agent, typing.Awaitable[gcdc_agent.Agent]], + ]: + raise NotImplementedError() + + @property + def update_agent( + self, + ) -> typing.Callable[ + [gcdc_agent.UpdateAgentRequest], + typing.Union[gcdc_agent.Agent, typing.Awaitable[gcdc_agent.Agent]], + ]: + raise NotImplementedError() + + @property + def delete_agent( + self, + ) -> typing.Callable[ + [agent.DeleteAgentRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + @property + def export_agent( + self, + ) -> typing.Callable[ + [agent.ExportAgentRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + + @property + def restore_agent( + self, + ) -> typing.Callable[ + [agent.RestoreAgentRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + + +__all__ = ("AgentsTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/agents/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/agents/transports/grpc.py new file mode 100644 index 00000000..bbf09619 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/agents/transports/grpc.py @@ -0,0 +1,435 @@ +# -*- 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 operations_v1 # 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.dialogflowcx_v3.types import agent +from google.cloud.dialogflowcx_v3.types import agent as gcdc_agent +from google.longrunning import operations_pb2 as operations # type: ignore +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import AgentsTransport, DEFAULT_CLIENT_INFO + + +class AgentsGrpcTransport(AgentsTransport): + """gRPC backend transport for Agents. + + Service for managing [Agents][google.cloud.dialogflow.cx.v3.Agent]. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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 operations_client(self) -> operations_v1.OperationsClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if "operations_client" not in self.__dict__: + self.__dict__["operations_client"] = operations_v1.OperationsClient( + self.grpc_channel + ) + + # Return the client from cache. + return self.__dict__["operations_client"] + + @property + def list_agents( + self, + ) -> Callable[[agent.ListAgentsRequest], agent.ListAgentsResponse]: + r"""Return a callable for the list agents method over gRPC. + + Returns the list of all agents in the specified + location. + + Returns: + Callable[[~.ListAgentsRequest], + ~.ListAgentsResponse]: + 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_agents" not in self._stubs: + self._stubs["list_agents"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/ListAgents", + request_serializer=agent.ListAgentsRequest.serialize, + response_deserializer=agent.ListAgentsResponse.deserialize, + ) + return self._stubs["list_agents"] + + @property + def get_agent(self) -> Callable[[agent.GetAgentRequest], agent.Agent]: + r"""Return a callable for the get agent method over gRPC. + + Retrieves the specified agent. + + Returns: + Callable[[~.GetAgentRequest], + ~.Agent]: + 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_agent" not in self._stubs: + self._stubs["get_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/GetAgent", + request_serializer=agent.GetAgentRequest.serialize, + response_deserializer=agent.Agent.deserialize, + ) + return self._stubs["get_agent"] + + @property + def create_agent( + self, + ) -> Callable[[gcdc_agent.CreateAgentRequest], gcdc_agent.Agent]: + r"""Return a callable for the create agent method over gRPC. + + Creates an agent in the specified location. + + Returns: + Callable[[~.CreateAgentRequest], + ~.Agent]: + 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_agent" not in self._stubs: + self._stubs["create_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/CreateAgent", + request_serializer=gcdc_agent.CreateAgentRequest.serialize, + response_deserializer=gcdc_agent.Agent.deserialize, + ) + return self._stubs["create_agent"] + + @property + def update_agent( + self, + ) -> Callable[[gcdc_agent.UpdateAgentRequest], gcdc_agent.Agent]: + r"""Return a callable for the update agent method over gRPC. + + Updates the specified agent. + + Returns: + Callable[[~.UpdateAgentRequest], + ~.Agent]: + 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_agent" not in self._stubs: + self._stubs["update_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/UpdateAgent", + request_serializer=gcdc_agent.UpdateAgentRequest.serialize, + response_deserializer=gcdc_agent.Agent.deserialize, + ) + return self._stubs["update_agent"] + + @property + def delete_agent(self) -> Callable[[agent.DeleteAgentRequest], empty.Empty]: + r"""Return a callable for the delete agent method over gRPC. + + Deletes the specified agent. + + Returns: + Callable[[~.DeleteAgentRequest], + ~.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_agent" not in self._stubs: + self._stubs["delete_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/DeleteAgent", + request_serializer=agent.DeleteAgentRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_agent"] + + @property + def export_agent( + self, + ) -> Callable[[agent.ExportAgentRequest], operations.Operation]: + r"""Return a callable for the export agent method over gRPC. + + Exports the specified agent to a binary file. + + Returns: + Callable[[~.ExportAgentRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "export_agent" not in self._stubs: + self._stubs["export_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/ExportAgent", + request_serializer=agent.ExportAgentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["export_agent"] + + @property + def restore_agent( + self, + ) -> Callable[[agent.RestoreAgentRequest], operations.Operation]: + r"""Return a callable for the restore agent method over gRPC. + + Restores the specified agent from a binary file. + Replaces the current agent with a new one. Note that all + existing resources in agent (e.g. intents, entity types, + flows) will be removed. + + Returns: + Callable[[~.RestoreAgentRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "restore_agent" not in self._stubs: + self._stubs["restore_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/RestoreAgent", + request_serializer=agent.RestoreAgentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["restore_agent"] + + +__all__ = ("AgentsGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/agents/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/agents/transports/grpc_asyncio.py new file mode 100644 index 00000000..8c5b8e48 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/agents/transports/grpc_asyncio.py @@ -0,0 +1,441 @@ +# -*- 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.api_core import operations_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 grpc.experimental import aio # type: ignore + +from google.cloud.dialogflowcx_v3.types import agent +from google.cloud.dialogflowcx_v3.types import agent as gcdc_agent +from google.longrunning import operations_pb2 as operations # type: ignore +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import AgentsTransport, DEFAULT_CLIENT_INFO +from .grpc import AgentsGrpcTransport + + +class AgentsGrpcAsyncIOTransport(AgentsTransport): + """gRPC AsyncIO backend transport for Agents. + + Service for managing [Agents][google.cloud.dialogflow.cx.v3.Agent]. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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 operations_client(self) -> operations_v1.OperationsAsyncClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if "operations_client" not in self.__dict__: + self.__dict__["operations_client"] = operations_v1.OperationsAsyncClient( + self.grpc_channel + ) + + # Return the client from cache. + return self.__dict__["operations_client"] + + @property + def list_agents( + self, + ) -> Callable[[agent.ListAgentsRequest], Awaitable[agent.ListAgentsResponse]]: + r"""Return a callable for the list agents method over gRPC. + + Returns the list of all agents in the specified + location. + + Returns: + Callable[[~.ListAgentsRequest], + Awaitable[~.ListAgentsResponse]]: + 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_agents" not in self._stubs: + self._stubs["list_agents"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/ListAgents", + request_serializer=agent.ListAgentsRequest.serialize, + response_deserializer=agent.ListAgentsResponse.deserialize, + ) + return self._stubs["list_agents"] + + @property + def get_agent(self) -> Callable[[agent.GetAgentRequest], Awaitable[agent.Agent]]: + r"""Return a callable for the get agent method over gRPC. + + Retrieves the specified agent. + + Returns: + Callable[[~.GetAgentRequest], + Awaitable[~.Agent]]: + 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_agent" not in self._stubs: + self._stubs["get_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/GetAgent", + request_serializer=agent.GetAgentRequest.serialize, + response_deserializer=agent.Agent.deserialize, + ) + return self._stubs["get_agent"] + + @property + def create_agent( + self, + ) -> Callable[[gcdc_agent.CreateAgentRequest], Awaitable[gcdc_agent.Agent]]: + r"""Return a callable for the create agent method over gRPC. + + Creates an agent in the specified location. + + Returns: + Callable[[~.CreateAgentRequest], + Awaitable[~.Agent]]: + 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_agent" not in self._stubs: + self._stubs["create_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/CreateAgent", + request_serializer=gcdc_agent.CreateAgentRequest.serialize, + response_deserializer=gcdc_agent.Agent.deserialize, + ) + return self._stubs["create_agent"] + + @property + def update_agent( + self, + ) -> Callable[[gcdc_agent.UpdateAgentRequest], Awaitable[gcdc_agent.Agent]]: + r"""Return a callable for the update agent method over gRPC. + + Updates the specified agent. + + Returns: + Callable[[~.UpdateAgentRequest], + Awaitable[~.Agent]]: + 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_agent" not in self._stubs: + self._stubs["update_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/UpdateAgent", + request_serializer=gcdc_agent.UpdateAgentRequest.serialize, + response_deserializer=gcdc_agent.Agent.deserialize, + ) + return self._stubs["update_agent"] + + @property + def delete_agent( + self, + ) -> Callable[[agent.DeleteAgentRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete agent method over gRPC. + + Deletes the specified agent. + + Returns: + Callable[[~.DeleteAgentRequest], + 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_agent" not in self._stubs: + self._stubs["delete_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/DeleteAgent", + request_serializer=agent.DeleteAgentRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_agent"] + + @property + def export_agent( + self, + ) -> Callable[[agent.ExportAgentRequest], Awaitable[operations.Operation]]: + r"""Return a callable for the export agent method over gRPC. + + Exports the specified agent to a binary file. + + Returns: + Callable[[~.ExportAgentRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "export_agent" not in self._stubs: + self._stubs["export_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/ExportAgent", + request_serializer=agent.ExportAgentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["export_agent"] + + @property + def restore_agent( + self, + ) -> Callable[[agent.RestoreAgentRequest], Awaitable[operations.Operation]]: + r"""Return a callable for the restore agent method over gRPC. + + Restores the specified agent from a binary file. + Replaces the current agent with a new one. Note that all + existing resources in agent (e.g. intents, entity types, + flows) will be removed. + + Returns: + Callable[[~.RestoreAgentRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "restore_agent" not in self._stubs: + self._stubs["restore_agent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Agents/RestoreAgent", + request_serializer=agent.RestoreAgentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["restore_agent"] + + +__all__ = ("AgentsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/entity_types/__init__.py b/google/cloud/dialogflowcx_v3/services/entity_types/__init__.py new file mode 100644 index 00000000..f52849c6 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/entity_types/__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 EntityTypesClient +from .async_client import EntityTypesAsyncClient + +__all__ = ( + "EntityTypesClient", + "EntityTypesAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/entity_types/async_client.py b/google/cloud/dialogflowcx_v3/services/entity_types/async_client.py new file mode 100644 index 00000000..3397af5b --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/entity_types/async_client.py @@ -0,0 +1,635 @@ +# -*- 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.dialogflowcx_v3.services.entity_types import pagers +from google.cloud.dialogflowcx_v3.types import entity_type +from google.cloud.dialogflowcx_v3.types import entity_type as gcdc_entity_type +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import EntityTypesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import EntityTypesGrpcAsyncIOTransport +from .client import EntityTypesClient + + +class EntityTypesAsyncClient: + """Service for managing + [EntityTypes][google.cloud.dialogflow.cx.v3.EntityType]. + """ + + _client: EntityTypesClient + + DEFAULT_ENDPOINT = EntityTypesClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = EntityTypesClient.DEFAULT_MTLS_ENDPOINT + + entity_type_path = staticmethod(EntityTypesClient.entity_type_path) + parse_entity_type_path = staticmethod(EntityTypesClient.parse_entity_type_path) + + common_billing_account_path = staticmethod( + EntityTypesClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + EntityTypesClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(EntityTypesClient.common_folder_path) + parse_common_folder_path = staticmethod(EntityTypesClient.parse_common_folder_path) + + common_organization_path = staticmethod(EntityTypesClient.common_organization_path) + parse_common_organization_path = staticmethod( + EntityTypesClient.parse_common_organization_path + ) + + common_project_path = staticmethod(EntityTypesClient.common_project_path) + parse_common_project_path = staticmethod( + EntityTypesClient.parse_common_project_path + ) + + common_location_path = staticmethod(EntityTypesClient.common_location_path) + parse_common_location_path = staticmethod( + EntityTypesClient.parse_common_location_path + ) + + from_service_account_file = EntityTypesClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> EntityTypesTransport: + """Return the transport used by the client instance. + + Returns: + EntityTypesTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(EntityTypesClient).get_transport_class, type(EntityTypesClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, EntityTypesTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the entity types 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, ~.EntityTypesTransport]): 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 = EntityTypesClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_entity_types( + self, + request: entity_type.ListEntityTypesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListEntityTypesAsyncPager: + r"""Returns the list of all entity types in the specified + agent. + + Args: + request (:class:`~.entity_type.ListEntityTypesRequest`): + The request object. The request message for + [EntityTypes.ListEntityTypes][google.cloud.dialogflow.cx.v3.EntityTypes.ListEntityTypes]. + parent (:class:`str`): + Required. The agent to list all entity types for. + Format: + ``projects//locations//agents/``. + 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: + ~.pagers.ListEntityTypesAsyncPager: + The response message for + [EntityTypes.ListEntityTypes][google.cloud.dialogflow.cx.v3.EntityTypes.ListEntityTypes]. + + 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 = entity_type.ListEntityTypesRequest(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_entity_types, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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.ListEntityTypesAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_entity_type( + self, + request: entity_type.GetEntityTypeRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> entity_type.EntityType: + r"""Retrieves the specified entity type. + + Args: + request (:class:`~.entity_type.GetEntityTypeRequest`): + The request object. The request message for + [EntityTypes.GetEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.GetEntityType]. + name (:class:`str`): + Required. The name of the entity type. Format: + ``projects//locations//agents//entityTypes/``. + 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: + ~.entity_type.EntityType: + Entities are extracted from user input and represent + parameters that are meaningful to your application. For + example, a date range, a proper name such as a + geographic location or landmark, and so on. Entities + represent actionable data for your application. + + When you define an entity, you can also include synonyms + that all map to that entity. For example, "soft drink", + "soda", "pop", and so on. + + There are three types of entities: + + - **System** - entities that are defined by the + Dialogflow API for common data types such as date, + time, currency, and so on. A system entity is + represented by the ``EntityType`` type. + + - **Custom** - entities that are defined by you that + represent actionable data that is meaningful to your + application. For example, you could define a + ``pizza.sauce`` entity for red or white pizza sauce, + a ``pizza.cheese`` entity for the different types of + cheese on a pizza, a ``pizza.topping`` entity for + different toppings, and so on. A custom entity is + represented by the ``EntityType`` type. + + - **User** - entities that are built for an individual + user such as favorites, preferences, playlists, and + so on. A user entity is represented by the + [SessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityType] + type. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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 = entity_type.GetEntityTypeRequest(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_entity_type, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_entity_type( + self, + request: gcdc_entity_type.CreateEntityTypeRequest = None, + *, + parent: str = None, + entity_type: gcdc_entity_type.EntityType = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_entity_type.EntityType: + r"""Creates an entity type in the specified agent. + + Args: + request (:class:`~.gcdc_entity_type.CreateEntityTypeRequest`): + The request object. The request message for + [EntityTypes.CreateEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.CreateEntityType]. + parent (:class:`str`): + Required. The agent to create a entity type for. Format: + ``projects//locations//agents/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + entity_type (:class:`~.gcdc_entity_type.EntityType`): + Required. The entity type to create. + This corresponds to the ``entity_type`` 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: + ~.gcdc_entity_type.EntityType: + Entities are extracted from user input and represent + parameters that are meaningful to your application. For + example, a date range, a proper name such as a + geographic location or landmark, and so on. Entities + represent actionable data for your application. + + When you define an entity, you can also include synonyms + that all map to that entity. For example, "soft drink", + "soda", "pop", and so on. + + There are three types of entities: + + - **System** - entities that are defined by the + Dialogflow API for common data types such as date, + time, currency, and so on. A system entity is + represented by the ``EntityType`` type. + + - **Custom** - entities that are defined by you that + represent actionable data that is meaningful to your + application. For example, you could define a + ``pizza.sauce`` entity for red or white pizza sauce, + a ``pizza.cheese`` entity for the different types of + cheese on a pizza, a ``pizza.topping`` entity for + different toppings, and so on. A custom entity is + represented by the ``EntityType`` type. + + - **User** - entities that are built for an individual + user such as favorites, preferences, playlists, and + so on. A user entity is represented by the + [SessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityType] + type. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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, entity_type]) + 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 = gcdc_entity_type.CreateEntityTypeRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if entity_type is not None: + request.entity_type = entity_type + + # 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_entity_type, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_entity_type( + self, + request: gcdc_entity_type.UpdateEntityTypeRequest = None, + *, + entity_type: gcdc_entity_type.EntityType = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_entity_type.EntityType: + r"""Updates the specified entity type. + + Args: + request (:class:`~.gcdc_entity_type.UpdateEntityTypeRequest`): + The request object. The request message for + [EntityTypes.UpdateEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.UpdateEntityType]. + entity_type (:class:`~.gcdc_entity_type.EntityType`): + Required. The entity type to update. + This corresponds to the ``entity_type`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_entity_type.EntityType: + Entities are extracted from user input and represent + parameters that are meaningful to your application. For + example, a date range, a proper name such as a + geographic location or landmark, and so on. Entities + represent actionable data for your application. + + When you define an entity, you can also include synonyms + that all map to that entity. For example, "soft drink", + "soda", "pop", and so on. + + There are three types of entities: + + - **System** - entities that are defined by the + Dialogflow API for common data types such as date, + time, currency, and so on. A system entity is + represented by the ``EntityType`` type. + + - **Custom** - entities that are defined by you that + represent actionable data that is meaningful to your + application. For example, you could define a + ``pizza.sauce`` entity for red or white pizza sauce, + a ``pizza.cheese`` entity for the different types of + cheese on a pizza, a ``pizza.topping`` entity for + different toppings, and so on. A custom entity is + represented by the ``EntityType`` type. + + - **User** - entities that are built for an individual + user such as favorites, preferences, playlists, and + so on. A user entity is represented by the + [SessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityType] + type. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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([entity_type, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = gcdc_entity_type.UpdateEntityTypeRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if entity_type is not None: + request.entity_type = entity_type + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_entity_type, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("entity_type.name", request.entity_type.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_entity_type( + self, + request: entity_type.DeleteEntityTypeRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified entity type. + + Args: + request (:class:`~.entity_type.DeleteEntityTypeRequest`): + The request object. The request message for + [EntityTypes.DeleteEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.DeleteEntityType]. + name (:class:`str`): + Required. The name of the entity type to delete. Format: + ``projects//locations//agents//entityTypes/``. + 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 = entity_type.DeleteEntityTypeRequest(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_entity_type, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("EntityTypesAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/entity_types/client.py b/google/cloud/dialogflowcx_v3/services/entity_types/client.py new file mode 100644 index 00000000..aacf1f5e --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/entity_types/client.py @@ -0,0 +1,835 @@ +# -*- 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.dialogflowcx_v3.services.entity_types import pagers +from google.cloud.dialogflowcx_v3.types import entity_type +from google.cloud.dialogflowcx_v3.types import entity_type as gcdc_entity_type +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import EntityTypesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import EntityTypesGrpcTransport +from .transports.grpc_asyncio import EntityTypesGrpcAsyncIOTransport + + +class EntityTypesClientMeta(type): + """Metaclass for the EntityTypes 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[EntityTypesTransport]] + _transport_registry["grpc"] = EntityTypesGrpcTransport + _transport_registry["grpc_asyncio"] = EntityTypesGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[EntityTypesTransport]: + """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 EntityTypesClient(metaclass=EntityTypesClientMeta): + """Service for managing + [EntityTypes][google.cloud.dialogflow.cx.v3.EntityType]. + """ + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> EntityTypesTransport: + """Return the transport used by the client instance. + + Returns: + EntityTypesTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def entity_type_path( + project: str, location: str, agent: str, entity_type: str, + ) -> str: + """Return a fully-qualified entity_type string.""" + return "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + + @staticmethod + def parse_entity_type_path(path: str) -> Dict[str, str]: + """Parse a entity_type path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/entityTypes/(?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, EntityTypesTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the entity types 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, ~.EntityTypesTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, EntityTypesTransport): + # transport is a EntityTypesTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_entity_types( + self, + request: entity_type.ListEntityTypesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListEntityTypesPager: + r"""Returns the list of all entity types in the specified + agent. + + Args: + request (:class:`~.entity_type.ListEntityTypesRequest`): + The request object. The request message for + [EntityTypes.ListEntityTypes][google.cloud.dialogflow.cx.v3.EntityTypes.ListEntityTypes]. + parent (:class:`str`): + Required. The agent to list all entity types for. + Format: + ``projects//locations//agents/``. + 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: + ~.pagers.ListEntityTypesPager: + The response message for + [EntityTypes.ListEntityTypes][google.cloud.dialogflow.cx.v3.EntityTypes.ListEntityTypes]. + + 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 entity_type.ListEntityTypesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, entity_type.ListEntityTypesRequest): + request = entity_type.ListEntityTypesRequest(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_entity_types] + + # 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.ListEntityTypesPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_entity_type( + self, + request: entity_type.GetEntityTypeRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> entity_type.EntityType: + r"""Retrieves the specified entity type. + + Args: + request (:class:`~.entity_type.GetEntityTypeRequest`): + The request object. The request message for + [EntityTypes.GetEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.GetEntityType]. + name (:class:`str`): + Required. The name of the entity type. Format: + ``projects//locations//agents//entityTypes/``. + 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: + ~.entity_type.EntityType: + Entities are extracted from user input and represent + parameters that are meaningful to your application. For + example, a date range, a proper name such as a + geographic location or landmark, and so on. Entities + represent actionable data for your application. + + When you define an entity, you can also include synonyms + that all map to that entity. For example, "soft drink", + "soda", "pop", and so on. + + There are three types of entities: + + - **System** - entities that are defined by the + Dialogflow API for common data types such as date, + time, currency, and so on. A system entity is + represented by the ``EntityType`` type. + + - **Custom** - entities that are defined by you that + represent actionable data that is meaningful to your + application. For example, you could define a + ``pizza.sauce`` entity for red or white pizza sauce, + a ``pizza.cheese`` entity for the different types of + cheese on a pizza, a ``pizza.topping`` entity for + different toppings, and so on. A custom entity is + represented by the ``EntityType`` type. + + - **User** - entities that are built for an individual + user such as favorites, preferences, playlists, and + so on. A user entity is represented by the + [SessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityType] + type. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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 entity_type.GetEntityTypeRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, entity_type.GetEntityTypeRequest): + request = entity_type.GetEntityTypeRequest(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_entity_type] + + # 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_entity_type( + self, + request: gcdc_entity_type.CreateEntityTypeRequest = None, + *, + parent: str = None, + entity_type: gcdc_entity_type.EntityType = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_entity_type.EntityType: + r"""Creates an entity type in the specified agent. + + Args: + request (:class:`~.gcdc_entity_type.CreateEntityTypeRequest`): + The request object. The request message for + [EntityTypes.CreateEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.CreateEntityType]. + parent (:class:`str`): + Required. The agent to create a entity type for. Format: + ``projects//locations//agents/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + entity_type (:class:`~.gcdc_entity_type.EntityType`): + Required. The entity type to create. + This corresponds to the ``entity_type`` 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: + ~.gcdc_entity_type.EntityType: + Entities are extracted from user input and represent + parameters that are meaningful to your application. For + example, a date range, a proper name such as a + geographic location or landmark, and so on. Entities + represent actionable data for your application. + + When you define an entity, you can also include synonyms + that all map to that entity. For example, "soft drink", + "soda", "pop", and so on. + + There are three types of entities: + + - **System** - entities that are defined by the + Dialogflow API for common data types such as date, + time, currency, and so on. A system entity is + represented by the ``EntityType`` type. + + - **Custom** - entities that are defined by you that + represent actionable data that is meaningful to your + application. For example, you could define a + ``pizza.sauce`` entity for red or white pizza sauce, + a ``pizza.cheese`` entity for the different types of + cheese on a pizza, a ``pizza.topping`` entity for + different toppings, and so on. A custom entity is + represented by the ``EntityType`` type. + + - **User** - entities that are built for an individual + user such as favorites, preferences, playlists, and + so on. A user entity is represented by the + [SessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityType] + type. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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, entity_type]) + 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 gcdc_entity_type.CreateEntityTypeRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_entity_type.CreateEntityTypeRequest): + request = gcdc_entity_type.CreateEntityTypeRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if entity_type is not None: + request.entity_type = entity_type + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_entity_type] + + # 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_entity_type( + self, + request: gcdc_entity_type.UpdateEntityTypeRequest = None, + *, + entity_type: gcdc_entity_type.EntityType = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_entity_type.EntityType: + r"""Updates the specified entity type. + + Args: + request (:class:`~.gcdc_entity_type.UpdateEntityTypeRequest`): + The request object. The request message for + [EntityTypes.UpdateEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.UpdateEntityType]. + entity_type (:class:`~.gcdc_entity_type.EntityType`): + Required. The entity type to update. + This corresponds to the ``entity_type`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_entity_type.EntityType: + Entities are extracted from user input and represent + parameters that are meaningful to your application. For + example, a date range, a proper name such as a + geographic location or landmark, and so on. Entities + represent actionable data for your application. + + When you define an entity, you can also include synonyms + that all map to that entity. For example, "soft drink", + "soda", "pop", and so on. + + There are three types of entities: + + - **System** - entities that are defined by the + Dialogflow API for common data types such as date, + time, currency, and so on. A system entity is + represented by the ``EntityType`` type. + + - **Custom** - entities that are defined by you that + represent actionable data that is meaningful to your + application. For example, you could define a + ``pizza.sauce`` entity for red or white pizza sauce, + a ``pizza.cheese`` entity for the different types of + cheese on a pizza, a ``pizza.topping`` entity for + different toppings, and so on. A custom entity is + represented by the ``EntityType`` type. + + - **User** - entities that are built for an individual + user such as favorites, preferences, playlists, and + so on. A user entity is represented by the + [SessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityType] + type. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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([entity_type, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a gcdc_entity_type.UpdateEntityTypeRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_entity_type.UpdateEntityTypeRequest): + request = gcdc_entity_type.UpdateEntityTypeRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if entity_type is not None: + request.entity_type = entity_type + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_entity_type] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("entity_type.name", request.entity_type.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def delete_entity_type( + self, + request: entity_type.DeleteEntityTypeRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified entity type. + + Args: + request (:class:`~.entity_type.DeleteEntityTypeRequest`): + The request object. The request message for + [EntityTypes.DeleteEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.DeleteEntityType]. + name (:class:`str`): + Required. The name of the entity type to delete. Format: + ``projects//locations//agents//entityTypes/``. + 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 entity_type.DeleteEntityTypeRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, entity_type.DeleteEntityTypeRequest): + request = entity_type.DeleteEntityTypeRequest(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_entity_type] + + # 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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("EntityTypesClient",) diff --git a/google/cloud/dialogflowcx_v3/services/entity_types/pagers.py b/google/cloud/dialogflowcx_v3/services/entity_types/pagers.py new file mode 100644 index 00000000..edafb73d --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/entity_types/pagers.py @@ -0,0 +1,148 @@ +# -*- 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 + +from google.cloud.dialogflowcx_v3.types import entity_type + + +class ListEntityTypesPager: + """A pager for iterating through ``list_entity_types`` requests. + + This class thinly wraps an initial + :class:`~.entity_type.ListEntityTypesResponse` object, and + provides an ``__iter__`` method to iterate through its + ``entity_types`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListEntityTypes`` requests and continue to iterate + through the ``entity_types`` field on the + corresponding responses. + + All the usual :class:`~.entity_type.ListEntityTypesResponse` + 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[..., entity_type.ListEntityTypesResponse], + request: entity_type.ListEntityTypesRequest, + response: entity_type.ListEntityTypesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.entity_type.ListEntityTypesRequest`): + The initial request object. + response (:class:`~.entity_type.ListEntityTypesResponse`): + 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 = entity_type.ListEntityTypesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[entity_type.ListEntityTypesResponse]: + 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[entity_type.EntityType]: + for page in self.pages: + yield from page.entity_types + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListEntityTypesAsyncPager: + """A pager for iterating through ``list_entity_types`` requests. + + This class thinly wraps an initial + :class:`~.entity_type.ListEntityTypesResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``entity_types`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListEntityTypes`` requests and continue to iterate + through the ``entity_types`` field on the + corresponding responses. + + All the usual :class:`~.entity_type.ListEntityTypesResponse` + 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[entity_type.ListEntityTypesResponse]], + request: entity_type.ListEntityTypesRequest, + response: entity_type.ListEntityTypesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.entity_type.ListEntityTypesRequest`): + The initial request object. + response (:class:`~.entity_type.ListEntityTypesResponse`): + 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 = entity_type.ListEntityTypesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[entity_type.ListEntityTypesResponse]: + 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[entity_type.EntityType]: + async def async_generator(): + async for page in self.pages: + for response in page.entity_types: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflowcx_v3/services/entity_types/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/entity_types/transports/__init__.py new file mode 100644 index 00000000..bd6250cb --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/entity_types/transports/__init__.py @@ -0,0 +1,36 @@ +# -*- 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 EntityTypesTransport +from .grpc import EntityTypesGrpcTransport +from .grpc_asyncio import EntityTypesGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[EntityTypesTransport]] +_transport_registry["grpc"] = EntityTypesGrpcTransport +_transport_registry["grpc_asyncio"] = EntityTypesGrpcAsyncIOTransport + + +__all__ = ( + "EntityTypesTransport", + "EntityTypesGrpcTransport", + "EntityTypesGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/entity_types/transports/base.py b/google/cloud/dialogflowcx_v3/services/entity_types/transports/base.py new file mode 100644 index 00000000..e3faf53a --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/entity_types/transports/base.py @@ -0,0 +1,184 @@ +# -*- 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.dialogflowcx_v3.types import entity_type +from google.cloud.dialogflowcx_v3.types import entity_type as gcdc_entity_type +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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class EntityTypesTransport(abc.ABC): + """Abstract transport class for EntityTypes.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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_entity_types: gapic_v1.method.wrap_method( + self.list_entity_types, default_timeout=None, client_info=client_info, + ), + self.get_entity_type: gapic_v1.method.wrap_method( + self.get_entity_type, default_timeout=None, client_info=client_info, + ), + self.create_entity_type: gapic_v1.method.wrap_method( + self.create_entity_type, default_timeout=None, client_info=client_info, + ), + self.update_entity_type: gapic_v1.method.wrap_method( + self.update_entity_type, default_timeout=None, client_info=client_info, + ), + self.delete_entity_type: gapic_v1.method.wrap_method( + self.delete_entity_type, default_timeout=None, client_info=client_info, + ), + } + + @property + def list_entity_types( + self, + ) -> typing.Callable[ + [entity_type.ListEntityTypesRequest], + typing.Union[ + entity_type.ListEntityTypesResponse, + typing.Awaitable[entity_type.ListEntityTypesResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_entity_type( + self, + ) -> typing.Callable[ + [entity_type.GetEntityTypeRequest], + typing.Union[entity_type.EntityType, typing.Awaitable[entity_type.EntityType]], + ]: + raise NotImplementedError() + + @property + def create_entity_type( + self, + ) -> typing.Callable[ + [gcdc_entity_type.CreateEntityTypeRequest], + typing.Union[ + gcdc_entity_type.EntityType, typing.Awaitable[gcdc_entity_type.EntityType] + ], + ]: + raise NotImplementedError() + + @property + def update_entity_type( + self, + ) -> typing.Callable[ + [gcdc_entity_type.UpdateEntityTypeRequest], + typing.Union[ + gcdc_entity_type.EntityType, typing.Awaitable[gcdc_entity_type.EntityType] + ], + ]: + raise NotImplementedError() + + @property + def delete_entity_type( + self, + ) -> typing.Callable[ + [entity_type.DeleteEntityTypeRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + +__all__ = ("EntityTypesTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/entity_types/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/entity_types/transports/grpc.py new file mode 100644 index 00000000..490b6fac --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/entity_types/transports/grpc.py @@ -0,0 +1,373 @@ +# -*- 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.dialogflowcx_v3.types import entity_type +from google.cloud.dialogflowcx_v3.types import entity_type as gcdc_entity_type +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import EntityTypesTransport, DEFAULT_CLIENT_INFO + + +class EntityTypesGrpcTransport(EntityTypesTransport): + """gRPC backend transport for EntityTypes. + + Service for managing + [EntityTypes][google.cloud.dialogflow.cx.v3.EntityType]. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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_entity_types( + self, + ) -> Callable[ + [entity_type.ListEntityTypesRequest], entity_type.ListEntityTypesResponse + ]: + r"""Return a callable for the list entity types method over gRPC. + + Returns the list of all entity types in the specified + agent. + + Returns: + Callable[[~.ListEntityTypesRequest], + ~.ListEntityTypesResponse]: + 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_entity_types" not in self._stubs: + self._stubs["list_entity_types"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.EntityTypes/ListEntityTypes", + request_serializer=entity_type.ListEntityTypesRequest.serialize, + response_deserializer=entity_type.ListEntityTypesResponse.deserialize, + ) + return self._stubs["list_entity_types"] + + @property + def get_entity_type( + self, + ) -> Callable[[entity_type.GetEntityTypeRequest], entity_type.EntityType]: + r"""Return a callable for the get entity type method over gRPC. + + Retrieves the specified entity type. + + Returns: + Callable[[~.GetEntityTypeRequest], + ~.EntityType]: + 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_entity_type" not in self._stubs: + self._stubs["get_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.EntityTypes/GetEntityType", + request_serializer=entity_type.GetEntityTypeRequest.serialize, + response_deserializer=entity_type.EntityType.deserialize, + ) + return self._stubs["get_entity_type"] + + @property + def create_entity_type( + self, + ) -> Callable[ + [gcdc_entity_type.CreateEntityTypeRequest], gcdc_entity_type.EntityType + ]: + r"""Return a callable for the create entity type method over gRPC. + + Creates an entity type in the specified agent. + + Returns: + Callable[[~.CreateEntityTypeRequest], + ~.EntityType]: + 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_entity_type" not in self._stubs: + self._stubs["create_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.EntityTypes/CreateEntityType", + request_serializer=gcdc_entity_type.CreateEntityTypeRequest.serialize, + response_deserializer=gcdc_entity_type.EntityType.deserialize, + ) + return self._stubs["create_entity_type"] + + @property + def update_entity_type( + self, + ) -> Callable[ + [gcdc_entity_type.UpdateEntityTypeRequest], gcdc_entity_type.EntityType + ]: + r"""Return a callable for the update entity type method over gRPC. + + Updates the specified entity type. + + Returns: + Callable[[~.UpdateEntityTypeRequest], + ~.EntityType]: + 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_entity_type" not in self._stubs: + self._stubs["update_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.EntityTypes/UpdateEntityType", + request_serializer=gcdc_entity_type.UpdateEntityTypeRequest.serialize, + response_deserializer=gcdc_entity_type.EntityType.deserialize, + ) + return self._stubs["update_entity_type"] + + @property + def delete_entity_type( + self, + ) -> Callable[[entity_type.DeleteEntityTypeRequest], empty.Empty]: + r"""Return a callable for the delete entity type method over gRPC. + + Deletes the specified entity type. + + Returns: + Callable[[~.DeleteEntityTypeRequest], + ~.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_entity_type" not in self._stubs: + self._stubs["delete_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.EntityTypes/DeleteEntityType", + request_serializer=entity_type.DeleteEntityTypeRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_entity_type"] + + +__all__ = ("EntityTypesGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/entity_types/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/entity_types/transports/grpc_asyncio.py new file mode 100644 index 00000000..5729cc64 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/entity_types/transports/grpc_asyncio.py @@ -0,0 +1,382 @@ +# -*- 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.dialogflowcx_v3.types import entity_type +from google.cloud.dialogflowcx_v3.types import entity_type as gcdc_entity_type +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import EntityTypesTransport, DEFAULT_CLIENT_INFO +from .grpc import EntityTypesGrpcTransport + + +class EntityTypesGrpcAsyncIOTransport(EntityTypesTransport): + """gRPC AsyncIO backend transport for EntityTypes. + + Service for managing + [EntityTypes][google.cloud.dialogflow.cx.v3.EntityType]. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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_entity_types( + self, + ) -> Callable[ + [entity_type.ListEntityTypesRequest], + Awaitable[entity_type.ListEntityTypesResponse], + ]: + r"""Return a callable for the list entity types method over gRPC. + + Returns the list of all entity types in the specified + agent. + + Returns: + Callable[[~.ListEntityTypesRequest], + Awaitable[~.ListEntityTypesResponse]]: + 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_entity_types" not in self._stubs: + self._stubs["list_entity_types"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.EntityTypes/ListEntityTypes", + request_serializer=entity_type.ListEntityTypesRequest.serialize, + response_deserializer=entity_type.ListEntityTypesResponse.deserialize, + ) + return self._stubs["list_entity_types"] + + @property + def get_entity_type( + self, + ) -> Callable[ + [entity_type.GetEntityTypeRequest], Awaitable[entity_type.EntityType] + ]: + r"""Return a callable for the get entity type method over gRPC. + + Retrieves the specified entity type. + + Returns: + Callable[[~.GetEntityTypeRequest], + Awaitable[~.EntityType]]: + 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_entity_type" not in self._stubs: + self._stubs["get_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.EntityTypes/GetEntityType", + request_serializer=entity_type.GetEntityTypeRequest.serialize, + response_deserializer=entity_type.EntityType.deserialize, + ) + return self._stubs["get_entity_type"] + + @property + def create_entity_type( + self, + ) -> Callable[ + [gcdc_entity_type.CreateEntityTypeRequest], + Awaitable[gcdc_entity_type.EntityType], + ]: + r"""Return a callable for the create entity type method over gRPC. + + Creates an entity type in the specified agent. + + Returns: + Callable[[~.CreateEntityTypeRequest], + Awaitable[~.EntityType]]: + 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_entity_type" not in self._stubs: + self._stubs["create_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.EntityTypes/CreateEntityType", + request_serializer=gcdc_entity_type.CreateEntityTypeRequest.serialize, + response_deserializer=gcdc_entity_type.EntityType.deserialize, + ) + return self._stubs["create_entity_type"] + + @property + def update_entity_type( + self, + ) -> Callable[ + [gcdc_entity_type.UpdateEntityTypeRequest], + Awaitable[gcdc_entity_type.EntityType], + ]: + r"""Return a callable for the update entity type method over gRPC. + + Updates the specified entity type. + + Returns: + Callable[[~.UpdateEntityTypeRequest], + Awaitable[~.EntityType]]: + 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_entity_type" not in self._stubs: + self._stubs["update_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.EntityTypes/UpdateEntityType", + request_serializer=gcdc_entity_type.UpdateEntityTypeRequest.serialize, + response_deserializer=gcdc_entity_type.EntityType.deserialize, + ) + return self._stubs["update_entity_type"] + + @property + def delete_entity_type( + self, + ) -> Callable[[entity_type.DeleteEntityTypeRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete entity type method over gRPC. + + Deletes the specified entity type. + + Returns: + Callable[[~.DeleteEntityTypeRequest], + 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_entity_type" not in self._stubs: + self._stubs["delete_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.EntityTypes/DeleteEntityType", + request_serializer=entity_type.DeleteEntityTypeRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_entity_type"] + + +__all__ = ("EntityTypesGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/environments/__init__.py b/google/cloud/dialogflowcx_v3/services/environments/__init__.py new file mode 100644 index 00000000..a67f2316 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/environments/__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 EnvironmentsClient +from .async_client import EnvironmentsAsyncClient + +__all__ = ( + "EnvironmentsClient", + "EnvironmentsAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/environments/async_client.py b/google/cloud/dialogflowcx_v3/services/environments/async_client.py new file mode 100644 index 00000000..edf94a12 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/environments/async_client.py @@ -0,0 +1,692 @@ +# -*- 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.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.dialogflowcx_v3.services.environments import pagers +from google.cloud.dialogflowcx_v3.types import environment +from google.cloud.dialogflowcx_v3.types import environment as gcdc_environment +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import EnvironmentsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import EnvironmentsGrpcAsyncIOTransport +from .client import EnvironmentsClient + + +class EnvironmentsAsyncClient: + """Service for managing + [Environments][google.cloud.dialogflow.cx.v3.Environment]. + """ + + _client: EnvironmentsClient + + DEFAULT_ENDPOINT = EnvironmentsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = EnvironmentsClient.DEFAULT_MTLS_ENDPOINT + + environment_path = staticmethod(EnvironmentsClient.environment_path) + parse_environment_path = staticmethod(EnvironmentsClient.parse_environment_path) + version_path = staticmethod(EnvironmentsClient.version_path) + parse_version_path = staticmethod(EnvironmentsClient.parse_version_path) + + common_billing_account_path = staticmethod( + EnvironmentsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + EnvironmentsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(EnvironmentsClient.common_folder_path) + parse_common_folder_path = staticmethod(EnvironmentsClient.parse_common_folder_path) + + common_organization_path = staticmethod(EnvironmentsClient.common_organization_path) + parse_common_organization_path = staticmethod( + EnvironmentsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(EnvironmentsClient.common_project_path) + parse_common_project_path = staticmethod( + EnvironmentsClient.parse_common_project_path + ) + + common_location_path = staticmethod(EnvironmentsClient.common_location_path) + parse_common_location_path = staticmethod( + EnvironmentsClient.parse_common_location_path + ) + + from_service_account_file = EnvironmentsClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> EnvironmentsTransport: + """Return the transport used by the client instance. + + Returns: + EnvironmentsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(EnvironmentsClient).get_transport_class, type(EnvironmentsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, EnvironmentsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the environments 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, ~.EnvironmentsTransport]): 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 = EnvironmentsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_environments( + self, + request: environment.ListEnvironmentsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListEnvironmentsAsyncPager: + r"""Returns the list of all environments in the specified + [Agent][google.cloud.dialogflow.cx.v3.Agent]. + + Args: + request (:class:`~.environment.ListEnvironmentsRequest`): + The request object. The request message for + [Environments.ListEnvironments][google.cloud.dialogflow.cx.v3.Environments.ListEnvironments]. + parent (:class:`str`): + Required. The + [Agent][google.cloud.dialogflow.cx.v3.Agent] to list all + environments for. Format: + ``projects//locations//agents/``. + 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: + ~.pagers.ListEnvironmentsAsyncPager: + The response message for + [Environments.ListEnvironments][google.cloud.dialogflow.cx.v3.Environments.ListEnvironments]. + + 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 = environment.ListEnvironmentsRequest(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_environments, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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.ListEnvironmentsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_environment( + self, + request: environment.GetEnvironmentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> environment.Environment: + r"""Retrieves the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Args: + request (:class:`~.environment.GetEnvironmentRequest`): + The request object. The request message for + [Environments.GetEnvironment][google.cloud.dialogflow.cx.v3.Environments.GetEnvironment]. + name (:class:`str`): + Required. The name of the + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + Format: + ``projects//locations//agents//environments/``. + 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: + ~.environment.Environment: + Represents an environment for an + agent. You can create multiple versions + of your agent and publish them to + separate environments. When you edit an + agent, you are editing the draft agent. + At any point, you can save the draft + agent as an agent version, which is an + immutable snapshot of your agent. When + you save the draft agent, it is + published to the default environment. + When you create agent versions, you can + publish them to custom environments. You + can create a variety of custom + environments for testing, development, + production, etc. + + """ + # 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 = environment.GetEnvironmentRequest(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_environment, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_environment( + self, + request: gcdc_environment.CreateEnvironmentRequest = None, + *, + parent: str = None, + environment: gcdc_environment.Environment = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Creates an + [Environment][google.cloud.dialogflow.cx.v3.Environment] in the + specified [Agent][google.cloud.dialogflow.cx.v3.Agent]. + + Args: + request (:class:`~.gcdc_environment.CreateEnvironmentRequest`): + The request object. The request message for + [Environments.CreateEnvironment][google.cloud.dialogflow.cx.v3.Environments.CreateEnvironment]. + parent (:class:`str`): + Required. The + [Agent][google.cloud.dialogflow.cx.v3.Agent] to create + an + [Environment][google.cloud.dialogflow.cx.v3.Environment] + for. Format: + ``projects//locations//agents/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + environment (:class:`~.gcdc_environment.Environment`): + Required. The environment to create. + This corresponds to the ``environment`` 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: + ~.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.gcdc_environment.Environment``: Represents an + environment for an agent. You can create multiple + versions of your agent and publish them to separate + environments. When you edit an agent, you are editing + the draft agent. At any point, you can save the draft + agent as an agent version, which is an immutable + snapshot of your agent. When you save the draft agent, + it is published to the default environment. When you + create agent versions, you can publish them to custom + environments. You can create a variety of custom + environments for testing, development, production, etc. + + """ + # 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, environment]) + 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 = gcdc_environment.CreateEnvironmentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if environment is not None: + request.environment = environment + + # 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_environment, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + gcdc_environment.Environment, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + async def update_environment( + self, + request: gcdc_environment.UpdateEnvironmentRequest = None, + *, + environment: gcdc_environment.Environment = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Updates the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Args: + request (:class:`~.gcdc_environment.UpdateEnvironmentRequest`): + The request object. The request message for + [Environments.UpdateEnvironment][google.cloud.dialogflow.cx.v3.Environments.UpdateEnvironment]. + environment (:class:`~.gcdc_environment.Environment`): + Required. The environment to update. + This corresponds to the ``environment`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + Required. The mask to control which + fields get updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.gcdc_environment.Environment``: Represents an + environment for an agent. You can create multiple + versions of your agent and publish them to separate + environments. When you edit an agent, you are editing + the draft agent. At any point, you can save the draft + agent as an agent version, which is an immutable + snapshot of your agent. When you save the draft agent, + it is published to the default environment. When you + create agent versions, you can publish them to custom + environments. You can create a variety of custom + environments for testing, development, production, etc. + + """ + # 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([environment, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = gcdc_environment.UpdateEnvironmentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if environment is not None: + request.environment = environment + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_environment, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("environment.name", request.environment.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + gcdc_environment.Environment, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + async def delete_environment( + self, + request: environment.DeleteEnvironmentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Args: + request (:class:`~.environment.DeleteEnvironmentRequest`): + The request object. The request message for + [Environments.DeleteEnvironment][google.cloud.dialogflow.cx.v3.Environments.DeleteEnvironment]. + name (:class:`str`): + Required. The name of the + [Environment][google.cloud.dialogflow.cx.v3.Environment] + to delete. Format: + ``projects//locations//agents//environments/``. + 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 = environment.DeleteEnvironmentRequest(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_environment, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + await rpc( + request, retry=retry, timeout=timeout, metadata=metadata, + ) + + async def lookup_environment_history( + self, + request: environment.LookupEnvironmentHistoryRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.LookupEnvironmentHistoryAsyncPager: + r"""Looks up the history of the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Args: + request (:class:`~.environment.LookupEnvironmentHistoryRequest`): + The request object. The request message for + [Environments.LookupEnvironmentHistory][google.cloud.dialogflow.cx.v3.Environments.LookupEnvironmentHistory]. + name (:class:`str`): + Required. Resource name of the environment to look up + the history for. Format: + ``projects//locations//agents//environments/``. + 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: + ~.pagers.LookupEnvironmentHistoryAsyncPager: + The response message for + [Environments.LookupEnvironmentHistory][google.cloud.dialogflow.cx.v3.Environments.LookupEnvironmentHistory]. + + 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([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 = environment.LookupEnvironmentHistoryRequest(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.lookup_environment_history, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # 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.LookupEnvironmentHistoryAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("EnvironmentsAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/environments/client.py b/google/cloud/dialogflowcx_v3/services/environments/client.py new file mode 100644 index 00000000..5acfe0b3 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/environments/client.py @@ -0,0 +1,911 @@ +# -*- 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.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.dialogflowcx_v3.services.environments import pagers +from google.cloud.dialogflowcx_v3.types import environment +from google.cloud.dialogflowcx_v3.types import environment as gcdc_environment +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import EnvironmentsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import EnvironmentsGrpcTransport +from .transports.grpc_asyncio import EnvironmentsGrpcAsyncIOTransport + + +class EnvironmentsClientMeta(type): + """Metaclass for the Environments 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[EnvironmentsTransport]] + _transport_registry["grpc"] = EnvironmentsGrpcTransport + _transport_registry["grpc_asyncio"] = EnvironmentsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[EnvironmentsTransport]: + """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 EnvironmentsClient(metaclass=EnvironmentsClientMeta): + """Service for managing + [Environments][google.cloud.dialogflow.cx.v3.Environment]. + """ + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> EnvironmentsTransport: + """Return the transport used by the client instance. + + Returns: + EnvironmentsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def environment_path( + project: str, location: str, agent: str, environment: str, + ) -> str: + """Return a fully-qualified environment string.""" + return "projects/{project}/locations/{location}/agents/{agent}/environments/{environment}".format( + project=project, location=location, agent=agent, environment=environment, + ) + + @staticmethod + def parse_environment_path(path: str) -> Dict[str, str]: + """Parse a environment path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/environments/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def version_path( + project: str, location: str, agent: str, flow: str, version: str, + ) -> str: + """Return a fully-qualified version string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/versions/{version}".format( + project=project, location=location, agent=agent, flow=flow, version=version, + ) + + @staticmethod + def parse_version_path(path: str) -> Dict[str, str]: + """Parse a version path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/versions/(?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, EnvironmentsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the environments 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, ~.EnvironmentsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, EnvironmentsTransport): + # transport is a EnvironmentsTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_environments( + self, + request: environment.ListEnvironmentsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListEnvironmentsPager: + r"""Returns the list of all environments in the specified + [Agent][google.cloud.dialogflow.cx.v3.Agent]. + + Args: + request (:class:`~.environment.ListEnvironmentsRequest`): + The request object. The request message for + [Environments.ListEnvironments][google.cloud.dialogflow.cx.v3.Environments.ListEnvironments]. + parent (:class:`str`): + Required. The + [Agent][google.cloud.dialogflow.cx.v3.Agent] to list all + environments for. Format: + ``projects//locations//agents/``. + 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: + ~.pagers.ListEnvironmentsPager: + The response message for + [Environments.ListEnvironments][google.cloud.dialogflow.cx.v3.Environments.ListEnvironments]. + + 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 environment.ListEnvironmentsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, environment.ListEnvironmentsRequest): + request = environment.ListEnvironmentsRequest(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_environments] + + # 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.ListEnvironmentsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_environment( + self, + request: environment.GetEnvironmentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> environment.Environment: + r"""Retrieves the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Args: + request (:class:`~.environment.GetEnvironmentRequest`): + The request object. The request message for + [Environments.GetEnvironment][google.cloud.dialogflow.cx.v3.Environments.GetEnvironment]. + name (:class:`str`): + Required. The name of the + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + Format: + ``projects//locations//agents//environments/``. + 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: + ~.environment.Environment: + Represents an environment for an + agent. You can create multiple versions + of your agent and publish them to + separate environments. When you edit an + agent, you are editing the draft agent. + At any point, you can save the draft + agent as an agent version, which is an + immutable snapshot of your agent. When + you save the draft agent, it is + published to the default environment. + When you create agent versions, you can + publish them to custom environments. You + can create a variety of custom + environments for testing, development, + production, etc. + + """ + # 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 environment.GetEnvironmentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, environment.GetEnvironmentRequest): + request = environment.GetEnvironmentRequest(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_environment] + + # 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_environment( + self, + request: gcdc_environment.CreateEnvironmentRequest = None, + *, + parent: str = None, + environment: gcdc_environment.Environment = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Creates an + [Environment][google.cloud.dialogflow.cx.v3.Environment] in the + specified [Agent][google.cloud.dialogflow.cx.v3.Agent]. + + Args: + request (:class:`~.gcdc_environment.CreateEnvironmentRequest`): + The request object. The request message for + [Environments.CreateEnvironment][google.cloud.dialogflow.cx.v3.Environments.CreateEnvironment]. + parent (:class:`str`): + Required. The + [Agent][google.cloud.dialogflow.cx.v3.Agent] to create + an + [Environment][google.cloud.dialogflow.cx.v3.Environment] + for. Format: + ``projects//locations//agents/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + environment (:class:`~.gcdc_environment.Environment`): + Required. The environment to create. + This corresponds to the ``environment`` 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: + ~.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.gcdc_environment.Environment``: Represents an + environment for an agent. You can create multiple + versions of your agent and publish them to separate + environments. When you edit an agent, you are editing + the draft agent. At any point, you can save the draft + agent as an agent version, which is an immutable + snapshot of your agent. When you save the draft agent, + it is published to the default environment. When you + create agent versions, you can publish them to custom + environments. You can create a variety of custom + environments for testing, development, production, etc. + + """ + # 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, environment]) + 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 gcdc_environment.CreateEnvironmentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_environment.CreateEnvironmentRequest): + request = gcdc_environment.CreateEnvironmentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if environment is not None: + request.environment = environment + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_environment] + + # 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,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + gcdc_environment.Environment, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + def update_environment( + self, + request: gcdc_environment.UpdateEnvironmentRequest = None, + *, + environment: gcdc_environment.Environment = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Updates the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Args: + request (:class:`~.gcdc_environment.UpdateEnvironmentRequest`): + The request object. The request message for + [Environments.UpdateEnvironment][google.cloud.dialogflow.cx.v3.Environments.UpdateEnvironment]. + environment (:class:`~.gcdc_environment.Environment`): + Required. The environment to update. + This corresponds to the ``environment`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + Required. The mask to control which + fields get updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.gcdc_environment.Environment``: Represents an + environment for an agent. You can create multiple + versions of your agent and publish them to separate + environments. When you edit an agent, you are editing + the draft agent. At any point, you can save the draft + agent as an agent version, which is an immutable + snapshot of your agent. When you save the draft agent, + it is published to the default environment. When you + create agent versions, you can publish them to custom + environments. You can create a variety of custom + environments for testing, development, production, etc. + + """ + # 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([environment, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a gcdc_environment.UpdateEnvironmentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_environment.UpdateEnvironmentRequest): + request = gcdc_environment.UpdateEnvironmentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if environment is not None: + request.environment = environment + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_environment] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("environment.name", request.environment.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + gcdc_environment.Environment, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + def delete_environment( + self, + request: environment.DeleteEnvironmentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Args: + request (:class:`~.environment.DeleteEnvironmentRequest`): + The request object. The request message for + [Environments.DeleteEnvironment][google.cloud.dialogflow.cx.v3.Environments.DeleteEnvironment]. + name (:class:`str`): + Required. The name of the + [Environment][google.cloud.dialogflow.cx.v3.Environment] + to delete. Format: + ``projects//locations//agents//environments/``. + 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 environment.DeleteEnvironmentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, environment.DeleteEnvironmentRequest): + request = environment.DeleteEnvironmentRequest(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_environment] + + # 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, + ) + + def lookup_environment_history( + self, + request: environment.LookupEnvironmentHistoryRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.LookupEnvironmentHistoryPager: + r"""Looks up the history of the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Args: + request (:class:`~.environment.LookupEnvironmentHistoryRequest`): + The request object. The request message for + [Environments.LookupEnvironmentHistory][google.cloud.dialogflow.cx.v3.Environments.LookupEnvironmentHistory]. + name (:class:`str`): + Required. Resource name of the environment to look up + the history for. Format: + ``projects//locations//agents//environments/``. + 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: + ~.pagers.LookupEnvironmentHistoryPager: + The response message for + [Environments.LookupEnvironmentHistory][google.cloud.dialogflow.cx.v3.Environments.LookupEnvironmentHistory]. + + 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([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 environment.LookupEnvironmentHistoryRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, environment.LookupEnvironmentHistoryRequest): + request = environment.LookupEnvironmentHistoryRequest(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.lookup_environment_history + ] + + # 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,) + + # This method is paged; wrap the response in a pager, which provides + # an `__iter__` convenience method. + response = pagers.LookupEnvironmentHistoryPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("EnvironmentsClient",) diff --git a/google/cloud/dialogflowcx_v3/services/environments/pagers.py b/google/cloud/dialogflowcx_v3/services/environments/pagers.py new file mode 100644 index 00000000..ce8409a4 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/environments/pagers.py @@ -0,0 +1,278 @@ +# -*- 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 + +from google.cloud.dialogflowcx_v3.types import environment + + +class ListEnvironmentsPager: + """A pager for iterating through ``list_environments`` requests. + + This class thinly wraps an initial + :class:`~.environment.ListEnvironmentsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``environments`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListEnvironments`` requests and continue to iterate + through the ``environments`` field on the + corresponding responses. + + All the usual :class:`~.environment.ListEnvironmentsResponse` + 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[..., environment.ListEnvironmentsResponse], + request: environment.ListEnvironmentsRequest, + response: environment.ListEnvironmentsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.environment.ListEnvironmentsRequest`): + The initial request object. + response (:class:`~.environment.ListEnvironmentsResponse`): + 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 = environment.ListEnvironmentsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[environment.ListEnvironmentsResponse]: + 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[environment.Environment]: + for page in self.pages: + yield from page.environments + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListEnvironmentsAsyncPager: + """A pager for iterating through ``list_environments`` requests. + + This class thinly wraps an initial + :class:`~.environment.ListEnvironmentsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``environments`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListEnvironments`` requests and continue to iterate + through the ``environments`` field on the + corresponding responses. + + All the usual :class:`~.environment.ListEnvironmentsResponse` + 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[environment.ListEnvironmentsResponse]], + request: environment.ListEnvironmentsRequest, + response: environment.ListEnvironmentsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.environment.ListEnvironmentsRequest`): + The initial request object. + response (:class:`~.environment.ListEnvironmentsResponse`): + 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 = environment.ListEnvironmentsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[environment.ListEnvironmentsResponse]: + 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[environment.Environment]: + async def async_generator(): + async for page in self.pages: + for response in page.environments: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class LookupEnvironmentHistoryPager: + """A pager for iterating through ``lookup_environment_history`` requests. + + This class thinly wraps an initial + :class:`~.environment.LookupEnvironmentHistoryResponse` object, and + provides an ``__iter__`` method to iterate through its + ``environments`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``LookupEnvironmentHistory`` requests and continue to iterate + through the ``environments`` field on the + corresponding responses. + + All the usual :class:`~.environment.LookupEnvironmentHistoryResponse` + 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[..., environment.LookupEnvironmentHistoryResponse], + request: environment.LookupEnvironmentHistoryRequest, + response: environment.LookupEnvironmentHistoryResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.environment.LookupEnvironmentHistoryRequest`): + The initial request object. + response (:class:`~.environment.LookupEnvironmentHistoryResponse`): + 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 = environment.LookupEnvironmentHistoryRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[environment.LookupEnvironmentHistoryResponse]: + 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[environment.Environment]: + for page in self.pages: + yield from page.environments + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class LookupEnvironmentHistoryAsyncPager: + """A pager for iterating through ``lookup_environment_history`` requests. + + This class thinly wraps an initial + :class:`~.environment.LookupEnvironmentHistoryResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``environments`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``LookupEnvironmentHistory`` requests and continue to iterate + through the ``environments`` field on the + corresponding responses. + + All the usual :class:`~.environment.LookupEnvironmentHistoryResponse` + 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[environment.LookupEnvironmentHistoryResponse]], + request: environment.LookupEnvironmentHistoryRequest, + response: environment.LookupEnvironmentHistoryResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.environment.LookupEnvironmentHistoryRequest`): + The initial request object. + response (:class:`~.environment.LookupEnvironmentHistoryResponse`): + 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 = environment.LookupEnvironmentHistoryRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages( + self, + ) -> AsyncIterable[environment.LookupEnvironmentHistoryResponse]: + 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[environment.Environment]: + async def async_generator(): + async for page in self.pages: + for response in page.environments: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflowcx_v3/services/environments/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/environments/transports/__init__.py new file mode 100644 index 00000000..14f04446 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/environments/transports/__init__.py @@ -0,0 +1,36 @@ +# -*- 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 EnvironmentsTransport +from .grpc import EnvironmentsGrpcTransport +from .grpc_asyncio import EnvironmentsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[EnvironmentsTransport]] +_transport_registry["grpc"] = EnvironmentsGrpcTransport +_transport_registry["grpc_asyncio"] = EnvironmentsGrpcAsyncIOTransport + + +__all__ = ( + "EnvironmentsTransport", + "EnvironmentsGrpcTransport", + "EnvironmentsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/environments/transports/base.py b/google/cloud/dialogflowcx_v3/services/environments/transports/base.py new file mode 100644 index 00000000..7f827332 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/environments/transports/base.py @@ -0,0 +1,206 @@ +# -*- 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.api_core import operations_v1 # type: ignore +from google.auth import credentials # type: ignore + +from google.cloud.dialogflowcx_v3.types import environment +from google.cloud.dialogflowcx_v3.types import environment as gcdc_environment +from google.longrunning import operations_pb2 as operations # type: ignore +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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class EnvironmentsTransport(abc.ABC): + """Abstract transport class for Environments.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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_environments: gapic_v1.method.wrap_method( + self.list_environments, default_timeout=None, client_info=client_info, + ), + self.get_environment: gapic_v1.method.wrap_method( + self.get_environment, default_timeout=None, client_info=client_info, + ), + self.create_environment: gapic_v1.method.wrap_method( + self.create_environment, default_timeout=None, client_info=client_info, + ), + self.update_environment: gapic_v1.method.wrap_method( + self.update_environment, default_timeout=None, client_info=client_info, + ), + self.delete_environment: gapic_v1.method.wrap_method( + self.delete_environment, default_timeout=None, client_info=client_info, + ), + self.lookup_environment_history: gapic_v1.method.wrap_method( + self.lookup_environment_history, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Return the client designed to process long-running operations.""" + raise NotImplementedError() + + @property + def list_environments( + self, + ) -> typing.Callable[ + [environment.ListEnvironmentsRequest], + typing.Union[ + environment.ListEnvironmentsResponse, + typing.Awaitable[environment.ListEnvironmentsResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_environment( + self, + ) -> typing.Callable[ + [environment.GetEnvironmentRequest], + typing.Union[ + environment.Environment, typing.Awaitable[environment.Environment] + ], + ]: + raise NotImplementedError() + + @property + def create_environment( + self, + ) -> typing.Callable[ + [gcdc_environment.CreateEnvironmentRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + + @property + def update_environment( + self, + ) -> typing.Callable[ + [gcdc_environment.UpdateEnvironmentRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + + @property + def delete_environment( + self, + ) -> typing.Callable[ + [environment.DeleteEnvironmentRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + @property + def lookup_environment_history( + self, + ) -> typing.Callable[ + [environment.LookupEnvironmentHistoryRequest], + typing.Union[ + environment.LookupEnvironmentHistoryResponse, + typing.Awaitable[environment.LookupEnvironmentHistoryResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("EnvironmentsTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/environments/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/environments/transports/grpc.py new file mode 100644 index 00000000..3086e047 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/environments/transports/grpc.py @@ -0,0 +1,422 @@ +# -*- 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 operations_v1 # 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.dialogflowcx_v3.types import environment +from google.cloud.dialogflowcx_v3.types import environment as gcdc_environment +from google.longrunning import operations_pb2 as operations # type: ignore +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import EnvironmentsTransport, DEFAULT_CLIENT_INFO + + +class EnvironmentsGrpcTransport(EnvironmentsTransport): + """gRPC backend transport for Environments. + + Service for managing + [Environments][google.cloud.dialogflow.cx.v3.Environment]. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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 operations_client(self) -> operations_v1.OperationsClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if "operations_client" not in self.__dict__: + self.__dict__["operations_client"] = operations_v1.OperationsClient( + self.grpc_channel + ) + + # Return the client from cache. + return self.__dict__["operations_client"] + + @property + def list_environments( + self, + ) -> Callable[ + [environment.ListEnvironmentsRequest], environment.ListEnvironmentsResponse + ]: + r"""Return a callable for the list environments method over gRPC. + + Returns the list of all environments in the specified + [Agent][google.cloud.dialogflow.cx.v3.Agent]. + + Returns: + Callable[[~.ListEnvironmentsRequest], + ~.ListEnvironmentsResponse]: + 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_environments" not in self._stubs: + self._stubs["list_environments"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/ListEnvironments", + request_serializer=environment.ListEnvironmentsRequest.serialize, + response_deserializer=environment.ListEnvironmentsResponse.deserialize, + ) + return self._stubs["list_environments"] + + @property + def get_environment( + self, + ) -> Callable[[environment.GetEnvironmentRequest], environment.Environment]: + r"""Return a callable for the get environment method over gRPC. + + Retrieves the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Returns: + Callable[[~.GetEnvironmentRequest], + ~.Environment]: + 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_environment" not in self._stubs: + self._stubs["get_environment"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/GetEnvironment", + request_serializer=environment.GetEnvironmentRequest.serialize, + response_deserializer=environment.Environment.deserialize, + ) + return self._stubs["get_environment"] + + @property + def create_environment( + self, + ) -> Callable[[gcdc_environment.CreateEnvironmentRequest], operations.Operation]: + r"""Return a callable for the create environment method over gRPC. + + Creates an + [Environment][google.cloud.dialogflow.cx.v3.Environment] in the + specified [Agent][google.cloud.dialogflow.cx.v3.Agent]. + + Returns: + Callable[[~.CreateEnvironmentRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_environment" not in self._stubs: + self._stubs["create_environment"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/CreateEnvironment", + request_serializer=gcdc_environment.CreateEnvironmentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["create_environment"] + + @property + def update_environment( + self, + ) -> Callable[[gcdc_environment.UpdateEnvironmentRequest], operations.Operation]: + r"""Return a callable for the update environment method over gRPC. + + Updates the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Returns: + Callable[[~.UpdateEnvironmentRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "update_environment" not in self._stubs: + self._stubs["update_environment"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/UpdateEnvironment", + request_serializer=gcdc_environment.UpdateEnvironmentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["update_environment"] + + @property + def delete_environment( + self, + ) -> Callable[[environment.DeleteEnvironmentRequest], empty.Empty]: + r"""Return a callable for the delete environment method over gRPC. + + Deletes the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Returns: + Callable[[~.DeleteEnvironmentRequest], + ~.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_environment" not in self._stubs: + self._stubs["delete_environment"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/DeleteEnvironment", + request_serializer=environment.DeleteEnvironmentRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_environment"] + + @property + def lookup_environment_history( + self, + ) -> Callable[ + [environment.LookupEnvironmentHistoryRequest], + environment.LookupEnvironmentHistoryResponse, + ]: + r"""Return a callable for the lookup environment history method over gRPC. + + Looks up the history of the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Returns: + Callable[[~.LookupEnvironmentHistoryRequest], + ~.LookupEnvironmentHistoryResponse]: + 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 "lookup_environment_history" not in self._stubs: + self._stubs["lookup_environment_history"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/LookupEnvironmentHistory", + request_serializer=environment.LookupEnvironmentHistoryRequest.serialize, + response_deserializer=environment.LookupEnvironmentHistoryResponse.deserialize, + ) + return self._stubs["lookup_environment_history"] + + +__all__ = ("EnvironmentsGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/environments/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/environments/transports/grpc_asyncio.py new file mode 100644 index 00000000..e6a2594f --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/environments/transports/grpc_asyncio.py @@ -0,0 +1,433 @@ +# -*- 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.api_core import operations_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 grpc.experimental import aio # type: ignore + +from google.cloud.dialogflowcx_v3.types import environment +from google.cloud.dialogflowcx_v3.types import environment as gcdc_environment +from google.longrunning import operations_pb2 as operations # type: ignore +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import EnvironmentsTransport, DEFAULT_CLIENT_INFO +from .grpc import EnvironmentsGrpcTransport + + +class EnvironmentsGrpcAsyncIOTransport(EnvironmentsTransport): + """gRPC AsyncIO backend transport for Environments. + + Service for managing + [Environments][google.cloud.dialogflow.cx.v3.Environment]. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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 operations_client(self) -> operations_v1.OperationsAsyncClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if "operations_client" not in self.__dict__: + self.__dict__["operations_client"] = operations_v1.OperationsAsyncClient( + self.grpc_channel + ) + + # Return the client from cache. + return self.__dict__["operations_client"] + + @property + def list_environments( + self, + ) -> Callable[ + [environment.ListEnvironmentsRequest], + Awaitable[environment.ListEnvironmentsResponse], + ]: + r"""Return a callable for the list environments method over gRPC. + + Returns the list of all environments in the specified + [Agent][google.cloud.dialogflow.cx.v3.Agent]. + + Returns: + Callable[[~.ListEnvironmentsRequest], + Awaitable[~.ListEnvironmentsResponse]]: + 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_environments" not in self._stubs: + self._stubs["list_environments"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/ListEnvironments", + request_serializer=environment.ListEnvironmentsRequest.serialize, + response_deserializer=environment.ListEnvironmentsResponse.deserialize, + ) + return self._stubs["list_environments"] + + @property + def get_environment( + self, + ) -> Callable[ + [environment.GetEnvironmentRequest], Awaitable[environment.Environment] + ]: + r"""Return a callable for the get environment method over gRPC. + + Retrieves the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Returns: + Callable[[~.GetEnvironmentRequest], + Awaitable[~.Environment]]: + 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_environment" not in self._stubs: + self._stubs["get_environment"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/GetEnvironment", + request_serializer=environment.GetEnvironmentRequest.serialize, + response_deserializer=environment.Environment.deserialize, + ) + return self._stubs["get_environment"] + + @property + def create_environment( + self, + ) -> Callable[ + [gcdc_environment.CreateEnvironmentRequest], Awaitable[operations.Operation] + ]: + r"""Return a callable for the create environment method over gRPC. + + Creates an + [Environment][google.cloud.dialogflow.cx.v3.Environment] in the + specified [Agent][google.cloud.dialogflow.cx.v3.Agent]. + + Returns: + Callable[[~.CreateEnvironmentRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_environment" not in self._stubs: + self._stubs["create_environment"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/CreateEnvironment", + request_serializer=gcdc_environment.CreateEnvironmentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["create_environment"] + + @property + def update_environment( + self, + ) -> Callable[ + [gcdc_environment.UpdateEnvironmentRequest], Awaitable[operations.Operation] + ]: + r"""Return a callable for the update environment method over gRPC. + + Updates the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Returns: + Callable[[~.UpdateEnvironmentRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "update_environment" not in self._stubs: + self._stubs["update_environment"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/UpdateEnvironment", + request_serializer=gcdc_environment.UpdateEnvironmentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["update_environment"] + + @property + def delete_environment( + self, + ) -> Callable[[environment.DeleteEnvironmentRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete environment method over gRPC. + + Deletes the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Returns: + Callable[[~.DeleteEnvironmentRequest], + 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_environment" not in self._stubs: + self._stubs["delete_environment"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/DeleteEnvironment", + request_serializer=environment.DeleteEnvironmentRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_environment"] + + @property + def lookup_environment_history( + self, + ) -> Callable[ + [environment.LookupEnvironmentHistoryRequest], + Awaitable[environment.LookupEnvironmentHistoryResponse], + ]: + r"""Return a callable for the lookup environment history method over gRPC. + + Looks up the history of the specified + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + + Returns: + Callable[[~.LookupEnvironmentHistoryRequest], + Awaitable[~.LookupEnvironmentHistoryResponse]]: + 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 "lookup_environment_history" not in self._stubs: + self._stubs["lookup_environment_history"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Environments/LookupEnvironmentHistory", + request_serializer=environment.LookupEnvironmentHistoryRequest.serialize, + response_deserializer=environment.LookupEnvironmentHistoryResponse.deserialize, + ) + return self._stubs["lookup_environment_history"] + + +__all__ = ("EnvironmentsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3beta1/types/ivr.py b/google/cloud/dialogflowcx_v3/services/flows/__init__.py similarity index 81% rename from google/cloud/dialogflowcx_v3beta1/types/ivr.py rename to google/cloud/dialogflowcx_v3/services/flows/__init__.py index 32c9622b..edf52773 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/ivr.py +++ b/google/cloud/dialogflowcx_v3/services/flows/__init__.py @@ -15,8 +15,10 @@ # limitations under the License. # +from .client import FlowsClient +from .async_client import FlowsAsyncClient -__protobuf__ = proto.module(package="google.cloud.dialogflow.cx.v3beta1", manifest={},) - - -__all__ = tuple(sorted(__protobuf__.manifest)) +__all__ = ( + "FlowsClient", + "FlowsAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/flows/async_client.py b/google/cloud/dialogflowcx_v3/services/flows/async_client.py new file mode 100644 index 00000000..31bfe6e6 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/flows/async_client.py @@ -0,0 +1,697 @@ +# -*- 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.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.dialogflowcx_v3.services.flows import pagers +from google.cloud.dialogflowcx_v3.types import flow +from google.cloud.dialogflowcx_v3.types import flow as gcdc_flow +from google.cloud.dialogflowcx_v3.types import page +from google.protobuf import empty_pb2 as empty # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore + +from .transports.base import FlowsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import FlowsGrpcAsyncIOTransport +from .client import FlowsClient + + +class FlowsAsyncClient: + """Service for managing [Flows][google.cloud.dialogflow.cx.v3.Flow].""" + + _client: FlowsClient + + DEFAULT_ENDPOINT = FlowsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = FlowsClient.DEFAULT_MTLS_ENDPOINT + + flow_path = staticmethod(FlowsClient.flow_path) + parse_flow_path = staticmethod(FlowsClient.parse_flow_path) + intent_path = staticmethod(FlowsClient.intent_path) + parse_intent_path = staticmethod(FlowsClient.parse_intent_path) + page_path = staticmethod(FlowsClient.page_path) + parse_page_path = staticmethod(FlowsClient.parse_page_path) + webhook_path = staticmethod(FlowsClient.webhook_path) + parse_webhook_path = staticmethod(FlowsClient.parse_webhook_path) + + common_billing_account_path = staticmethod(FlowsClient.common_billing_account_path) + parse_common_billing_account_path = staticmethod( + FlowsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(FlowsClient.common_folder_path) + parse_common_folder_path = staticmethod(FlowsClient.parse_common_folder_path) + + common_organization_path = staticmethod(FlowsClient.common_organization_path) + parse_common_organization_path = staticmethod( + FlowsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(FlowsClient.common_project_path) + parse_common_project_path = staticmethod(FlowsClient.parse_common_project_path) + + common_location_path = staticmethod(FlowsClient.common_location_path) + parse_common_location_path = staticmethod(FlowsClient.parse_common_location_path) + + from_service_account_file = FlowsClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> FlowsTransport: + """Return the transport used by the client instance. + + Returns: + FlowsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(FlowsClient).get_transport_class, type(FlowsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, FlowsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the flows 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, ~.FlowsTransport]): 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 = FlowsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def create_flow( + self, + request: gcdc_flow.CreateFlowRequest = None, + *, + parent: str = None, + flow: gcdc_flow.Flow = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_flow.Flow: + r"""Creates a flow in the specified agent. + + Args: + request (:class:`~.gcdc_flow.CreateFlowRequest`): + The request object. The request message for + [Flows.CreateFlow][google.cloud.dialogflow.cx.v3.Flows.CreateFlow]. + parent (:class:`str`): + Required. The agent to create a flow for. Format: + ``projects//locations//agents/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + flow (:class:`~.gcdc_flow.Flow`): + Required. The flow to create. + This corresponds to the ``flow`` 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: + ~.gcdc_flow.Flow: + Flows represents the conversation + flows when you build your chatbot agent. + A flow consists of many pages connected + by the transition routes. Conversations + always start with the built-in Start + Flow (with an all-0 ID). Transition + routes can direct the conversation + session from the current flow (parent + flow) to another flow (sub flow). When + the sub flow is finished, Dialogflow + will bring the session back to the + parent flow, where the sub flow is + started. + + Usually, when a transition route is + followed by a matched intent, the intent + will be "consumed". This means the + intent won't activate more transition + routes. However, when the followed + transition route moves the conversation + session into a different flow, the + matched intent can be carried over and + to be consumed in the target flow. + + """ + # 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, flow]) + 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 = gcdc_flow.CreateFlowRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if flow is not None: + request.flow = flow + + # 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_flow, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_flow( + self, + request: flow.DeleteFlowRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes a specified flow. + + Args: + request (:class:`~.flow.DeleteFlowRequest`): + The request object. The request message for + [Flows.DeleteFlow][google.cloud.dialogflow.cx.v3.Flows.DeleteFlow]. + name (:class:`str`): + Required. The name of the flow to delete. Format: + ``projects//locations//agents//flows/``. + 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 = flow.DeleteFlowRequest(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_flow, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + await rpc( + request, retry=retry, timeout=timeout, metadata=metadata, + ) + + async def list_flows( + self, + request: flow.ListFlowsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListFlowsAsyncPager: + r"""Returns the list of all flows in the specified agent. + + Args: + request (:class:`~.flow.ListFlowsRequest`): + The request object. The request message for + [Flows.ListFlows][google.cloud.dialogflow.cx.v3.Flows.ListFlows]. + parent (:class:`str`): + Required. The agent containing the flows. Format: + ``projects//locations//agents/``. + 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: + ~.pagers.ListFlowsAsyncPager: + The response message for + [Flows.ListFlows][google.cloud.dialogflow.cx.v3.Flows.ListFlows]. + + 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 = flow.ListFlowsRequest(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_flows, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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.ListFlowsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_flow( + self, + request: flow.GetFlowRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> flow.Flow: + r"""Retrieves the specified flow. + + Args: + request (:class:`~.flow.GetFlowRequest`): + The request object. The response message for + [Flows.GetFlow][google.cloud.dialogflow.cx.v3.Flows.GetFlow]. + name (:class:`str`): + Required. The name of the flow to get. Format: + ``projects//locations//agents//flows/``. + 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: + ~.flow.Flow: + Flows represents the conversation + flows when you build your chatbot agent. + A flow consists of many pages connected + by the transition routes. Conversations + always start with the built-in Start + Flow (with an all-0 ID). Transition + routes can direct the conversation + session from the current flow (parent + flow) to another flow (sub flow). When + the sub flow is finished, Dialogflow + will bring the session back to the + parent flow, where the sub flow is + started. + + Usually, when a transition route is + followed by a matched intent, the intent + will be "consumed". This means the + intent won't activate more transition + routes. However, when the followed + transition route moves the conversation + session into a different flow, the + matched intent can be carried over and + to be consumed in the target flow. + + """ + # 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 = flow.GetFlowRequest(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_flow, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def update_flow( + self, + request: gcdc_flow.UpdateFlowRequest = None, + *, + flow: gcdc_flow.Flow = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_flow.Flow: + r"""Updates the specified flow. + + Args: + request (:class:`~.gcdc_flow.UpdateFlowRequest`): + The request object. The request message for + [Flows.UpdateFlow][google.cloud.dialogflow.cx.v3.Flows.UpdateFlow]. + flow (:class:`~.gcdc_flow.Flow`): + Required. The flow to update. + This corresponds to the ``flow`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + Required. The mask to control which fields get updated. + If ``update_mask`` is not specified, an error will be + returned. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_flow.Flow: + Flows represents the conversation + flows when you build your chatbot agent. + A flow consists of many pages connected + by the transition routes. Conversations + always start with the built-in Start + Flow (with an all-0 ID). Transition + routes can direct the conversation + session from the current flow (parent + flow) to another flow (sub flow). When + the sub flow is finished, Dialogflow + will bring the session back to the + parent flow, where the sub flow is + started. + + Usually, when a transition route is + followed by a matched intent, the intent + will be "consumed". This means the + intent won't activate more transition + routes. However, when the followed + transition route moves the conversation + session into a different flow, the + matched intent can be carried over and + to be consumed in the target flow. + + """ + # 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([flow, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = gcdc_flow.UpdateFlowRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if flow is not None: + request.flow = flow + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_flow, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("flow.name", request.flow.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def train_flow( + self, + request: flow.TrainFlowRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Trains the specified flow. Note that only the flow in + 'draft' environment is trained. + + Args: + request (:class:`~.flow.TrainFlowRequest`): + The request object. The request message for + [Flows.TrainFlow][google.cloud.dialogflow.cx.v3.Flows.TrainFlow]. + name (:class:`str`): + Required. The flow to train. Format: + ``projects//locations//agents//flows/``. + 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: + ~.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.empty.Empty``: A generic empty message that + you can re-use to avoid defining duplicated empty + messages in your APIs. A typical example is to use it as + the request or the response type of an API method. For + instance: + + :: + + service Foo { + rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); + } + + The JSON representation for ``Empty`` is empty JSON + object ``{}``. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = flow.TrainFlowRequest(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.train_flow, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + empty.Empty, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("FlowsAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/flows/client.py b/google/cloud/dialogflowcx_v3/services/flows/client.py new file mode 100644 index 00000000..b2c5b800 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/flows/client.py @@ -0,0 +1,946 @@ +# -*- 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.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.dialogflowcx_v3.services.flows import pagers +from google.cloud.dialogflowcx_v3.types import flow +from google.cloud.dialogflowcx_v3.types import flow as gcdc_flow +from google.cloud.dialogflowcx_v3.types import page +from google.protobuf import empty_pb2 as empty # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore + +from .transports.base import FlowsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import FlowsGrpcTransport +from .transports.grpc_asyncio import FlowsGrpcAsyncIOTransport + + +class FlowsClientMeta(type): + """Metaclass for the Flows 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[FlowsTransport]] + _transport_registry["grpc"] = FlowsGrpcTransport + _transport_registry["grpc_asyncio"] = FlowsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[FlowsTransport]: + """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 FlowsClient(metaclass=FlowsClientMeta): + """Service for managing [Flows][google.cloud.dialogflow.cx.v3.Flow].""" + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> FlowsTransport: + """Return the transport used by the client instance. + + Returns: + FlowsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def flow_path(project: str, location: str, agent: str, flow: str,) -> str: + """Return a fully-qualified flow string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + + @staticmethod + def parse_flow_path(path: str) -> Dict[str, str]: + """Parse a flow path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def intent_path(project: str, location: str, agent: str, intent: str,) -> str: + """Return a fully-qualified intent string.""" + return "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + + @staticmethod + def parse_intent_path(path: str) -> Dict[str, str]: + """Parse a intent path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/intents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def page_path( + project: str, location: str, agent: str, flow: str, page: str, + ) -> str: + """Return a fully-qualified page string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + + @staticmethod + def parse_page_path(path: str) -> Dict[str, str]: + """Parse a page path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/pages/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def webhook_path(project: str, location: str, agent: str, webhook: str,) -> str: + """Return a fully-qualified webhook string.""" + return "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + + @staticmethod + def parse_webhook_path(path: str) -> Dict[str, str]: + """Parse a webhook path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/webhooks/(?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, FlowsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the flows 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, ~.FlowsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, FlowsTransport): + # transport is a FlowsTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def create_flow( + self, + request: gcdc_flow.CreateFlowRequest = None, + *, + parent: str = None, + flow: gcdc_flow.Flow = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_flow.Flow: + r"""Creates a flow in the specified agent. + + Args: + request (:class:`~.gcdc_flow.CreateFlowRequest`): + The request object. The request message for + [Flows.CreateFlow][google.cloud.dialogflow.cx.v3.Flows.CreateFlow]. + parent (:class:`str`): + Required. The agent to create a flow for. Format: + ``projects//locations//agents/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + flow (:class:`~.gcdc_flow.Flow`): + Required. The flow to create. + This corresponds to the ``flow`` 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: + ~.gcdc_flow.Flow: + Flows represents the conversation + flows when you build your chatbot agent. + A flow consists of many pages connected + by the transition routes. Conversations + always start with the built-in Start + Flow (with an all-0 ID). Transition + routes can direct the conversation + session from the current flow (parent + flow) to another flow (sub flow). When + the sub flow is finished, Dialogflow + will bring the session back to the + parent flow, where the sub flow is + started. + + Usually, when a transition route is + followed by a matched intent, the intent + will be "consumed". This means the + intent won't activate more transition + routes. However, when the followed + transition route moves the conversation + session into a different flow, the + matched intent can be carried over and + to be consumed in the target flow. + + """ + # 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, flow]) + 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 gcdc_flow.CreateFlowRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_flow.CreateFlowRequest): + request = gcdc_flow.CreateFlowRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if flow is not None: + request.flow = flow + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_flow] + + # 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 delete_flow( + self, + request: flow.DeleteFlowRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes a specified flow. + + Args: + request (:class:`~.flow.DeleteFlowRequest`): + The request object. The request message for + [Flows.DeleteFlow][google.cloud.dialogflow.cx.v3.Flows.DeleteFlow]. + name (:class:`str`): + Required. The name of the flow to delete. Format: + ``projects//locations//agents//flows/``. + 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 flow.DeleteFlowRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, flow.DeleteFlowRequest): + request = flow.DeleteFlowRequest(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_flow] + + # 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, + ) + + def list_flows( + self, + request: flow.ListFlowsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListFlowsPager: + r"""Returns the list of all flows in the specified agent. + + Args: + request (:class:`~.flow.ListFlowsRequest`): + The request object. The request message for + [Flows.ListFlows][google.cloud.dialogflow.cx.v3.Flows.ListFlows]. + parent (:class:`str`): + Required. The agent containing the flows. Format: + ``projects//locations//agents/``. + 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: + ~.pagers.ListFlowsPager: + The response message for + [Flows.ListFlows][google.cloud.dialogflow.cx.v3.Flows.ListFlows]. + + 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 flow.ListFlowsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, flow.ListFlowsRequest): + request = flow.ListFlowsRequest(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_flows] + + # 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.ListFlowsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_flow( + self, + request: flow.GetFlowRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> flow.Flow: + r"""Retrieves the specified flow. + + Args: + request (:class:`~.flow.GetFlowRequest`): + The request object. The response message for + [Flows.GetFlow][google.cloud.dialogflow.cx.v3.Flows.GetFlow]. + name (:class:`str`): + Required. The name of the flow to get. Format: + ``projects//locations//agents//flows/``. + 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: + ~.flow.Flow: + Flows represents the conversation + flows when you build your chatbot agent. + A flow consists of many pages connected + by the transition routes. Conversations + always start with the built-in Start + Flow (with an all-0 ID). Transition + routes can direct the conversation + session from the current flow (parent + flow) to another flow (sub flow). When + the sub flow is finished, Dialogflow + will bring the session back to the + parent flow, where the sub flow is + started. + + Usually, when a transition route is + followed by a matched intent, the intent + will be "consumed". This means the + intent won't activate more transition + routes. However, when the followed + transition route moves the conversation + session into a different flow, the + matched intent can be carried over and + to be consumed in the target flow. + + """ + # 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 flow.GetFlowRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, flow.GetFlowRequest): + request = flow.GetFlowRequest(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_flow] + + # 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 update_flow( + self, + request: gcdc_flow.UpdateFlowRequest = None, + *, + flow: gcdc_flow.Flow = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_flow.Flow: + r"""Updates the specified flow. + + Args: + request (:class:`~.gcdc_flow.UpdateFlowRequest`): + The request object. The request message for + [Flows.UpdateFlow][google.cloud.dialogflow.cx.v3.Flows.UpdateFlow]. + flow (:class:`~.gcdc_flow.Flow`): + Required. The flow to update. + This corresponds to the ``flow`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + Required. The mask to control which fields get updated. + If ``update_mask`` is not specified, an error will be + returned. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_flow.Flow: + Flows represents the conversation + flows when you build your chatbot agent. + A flow consists of many pages connected + by the transition routes. Conversations + always start with the built-in Start + Flow (with an all-0 ID). Transition + routes can direct the conversation + session from the current flow (parent + flow) to another flow (sub flow). When + the sub flow is finished, Dialogflow + will bring the session back to the + parent flow, where the sub flow is + started. + + Usually, when a transition route is + followed by a matched intent, the intent + will be "consumed". This means the + intent won't activate more transition + routes. However, when the followed + transition route moves the conversation + session into a different flow, the + matched intent can be carried over and + to be consumed in the target flow. + + """ + # 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([flow, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a gcdc_flow.UpdateFlowRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_flow.UpdateFlowRequest): + request = gcdc_flow.UpdateFlowRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if flow is not None: + request.flow = flow + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_flow] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("flow.name", request.flow.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def train_flow( + self, + request: flow.TrainFlowRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Trains the specified flow. Note that only the flow in + 'draft' environment is trained. + + Args: + request (:class:`~.flow.TrainFlowRequest`): + The request object. The request message for + [Flows.TrainFlow][google.cloud.dialogflow.cx.v3.Flows.TrainFlow]. + name (:class:`str`): + Required. The flow to train. Format: + ``projects//locations//agents//flows/``. + 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: + ~.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.empty.Empty``: A generic empty message that + you can re-use to avoid defining duplicated empty + messages in your APIs. A typical example is to use it as + the request or the response type of an API method. For + instance: + + :: + + service Foo { + rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); + } + + The JSON representation for ``Empty`` is empty JSON + object ``{}``. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a flow.TrainFlowRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, flow.TrainFlowRequest): + request = flow.TrainFlowRequest(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.train_flow] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + empty.Empty, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("FlowsClient",) diff --git a/google/cloud/dialogflowcx_v3/services/flows/pagers.py b/google/cloud/dialogflowcx_v3/services/flows/pagers.py new file mode 100644 index 00000000..bf55d3bb --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/flows/pagers.py @@ -0,0 +1,148 @@ +# -*- 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 + +from google.cloud.dialogflowcx_v3.types import flow + + +class ListFlowsPager: + """A pager for iterating through ``list_flows`` requests. + + This class thinly wraps an initial + :class:`~.flow.ListFlowsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``flows`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListFlows`` requests and continue to iterate + through the ``flows`` field on the + corresponding responses. + + All the usual :class:`~.flow.ListFlowsResponse` + 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[..., flow.ListFlowsResponse], + request: flow.ListFlowsRequest, + response: flow.ListFlowsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.flow.ListFlowsRequest`): + The initial request object. + response (:class:`~.flow.ListFlowsResponse`): + 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 = flow.ListFlowsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[flow.ListFlowsResponse]: + 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[flow.Flow]: + for page in self.pages: + yield from page.flows + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListFlowsAsyncPager: + """A pager for iterating through ``list_flows`` requests. + + This class thinly wraps an initial + :class:`~.flow.ListFlowsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``flows`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListFlows`` requests and continue to iterate + through the ``flows`` field on the + corresponding responses. + + All the usual :class:`~.flow.ListFlowsResponse` + 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[flow.ListFlowsResponse]], + request: flow.ListFlowsRequest, + response: flow.ListFlowsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.flow.ListFlowsRequest`): + The initial request object. + response (:class:`~.flow.ListFlowsResponse`): + 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 = flow.ListFlowsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[flow.ListFlowsResponse]: + 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[flow.Flow]: + async def async_generator(): + async for page in self.pages: + for response in page.flows: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflowcx_v3/services/flows/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/flows/transports/__init__.py new file mode 100644 index 00000000..d43aadd7 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/flows/transports/__init__.py @@ -0,0 +1,36 @@ +# -*- 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 FlowsTransport +from .grpc import FlowsGrpcTransport +from .grpc_asyncio import FlowsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[FlowsTransport]] +_transport_registry["grpc"] = FlowsGrpcTransport +_transport_registry["grpc_asyncio"] = FlowsGrpcAsyncIOTransport + + +__all__ = ( + "FlowsTransport", + "FlowsGrpcTransport", + "FlowsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/flows/transports/base.py b/google/cloud/dialogflowcx_v3/services/flows/transports/base.py new file mode 100644 index 00000000..750a522e --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/flows/transports/base.py @@ -0,0 +1,195 @@ +# -*- 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.api_core import operations_v1 # type: ignore +from google.auth import credentials # type: ignore + +from google.cloud.dialogflowcx_v3.types import flow +from google.cloud.dialogflowcx_v3.types import flow as gcdc_flow +from google.longrunning import operations_pb2 as operations # type: ignore +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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class FlowsTransport(abc.ABC): + """Abstract transport class for Flows.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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.create_flow: gapic_v1.method.wrap_method( + self.create_flow, default_timeout=None, client_info=client_info, + ), + self.delete_flow: gapic_v1.method.wrap_method( + self.delete_flow, default_timeout=None, client_info=client_info, + ), + self.list_flows: gapic_v1.method.wrap_method( + self.list_flows, default_timeout=None, client_info=client_info, + ), + self.get_flow: gapic_v1.method.wrap_method( + self.get_flow, default_timeout=None, client_info=client_info, + ), + self.update_flow: gapic_v1.method.wrap_method( + self.update_flow, default_timeout=None, client_info=client_info, + ), + self.train_flow: gapic_v1.method.wrap_method( + self.train_flow, default_timeout=None, client_info=client_info, + ), + } + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Return the client designed to process long-running operations.""" + raise NotImplementedError() + + @property + def create_flow( + self, + ) -> typing.Callable[ + [gcdc_flow.CreateFlowRequest], + typing.Union[gcdc_flow.Flow, typing.Awaitable[gcdc_flow.Flow]], + ]: + raise NotImplementedError() + + @property + def delete_flow( + self, + ) -> typing.Callable[ + [flow.DeleteFlowRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + @property + def list_flows( + self, + ) -> typing.Callable[ + [flow.ListFlowsRequest], + typing.Union[flow.ListFlowsResponse, typing.Awaitable[flow.ListFlowsResponse]], + ]: + raise NotImplementedError() + + @property + def get_flow( + self, + ) -> typing.Callable[ + [flow.GetFlowRequest], typing.Union[flow.Flow, typing.Awaitable[flow.Flow]] + ]: + raise NotImplementedError() + + @property + def update_flow( + self, + ) -> typing.Callable[ + [gcdc_flow.UpdateFlowRequest], + typing.Union[gcdc_flow.Flow, typing.Awaitable[gcdc_flow.Flow]], + ]: + raise NotImplementedError() + + @property + def train_flow( + self, + ) -> typing.Callable[ + [flow.TrainFlowRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + + +__all__ = ("FlowsTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/flows/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/flows/transports/grpc.py new file mode 100644 index 00000000..6efec1ad --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/flows/transports/grpc.py @@ -0,0 +1,398 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import warnings +from typing import Callable, Dict, Optional, Sequence, Tuple + +from google.api_core import grpc_helpers # type: ignore +from google.api_core import operations_v1 # type: ignore +from google.api_core import gapic_v1 # type: ignore +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.dialogflowcx_v3.types import flow +from google.cloud.dialogflowcx_v3.types import flow as gcdc_flow +from google.longrunning import operations_pb2 as operations # type: ignore +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import FlowsTransport, DEFAULT_CLIENT_INFO + + +class FlowsGrpcTransport(FlowsTransport): + """gRPC backend transport for Flows. + + Service for managing [Flows][google.cloud.dialogflow.cx.v3.Flow]. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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 operations_client(self) -> operations_v1.OperationsClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if "operations_client" not in self.__dict__: + self.__dict__["operations_client"] = operations_v1.OperationsClient( + self.grpc_channel + ) + + # Return the client from cache. + return self.__dict__["operations_client"] + + @property + def create_flow(self) -> Callable[[gcdc_flow.CreateFlowRequest], gcdc_flow.Flow]: + r"""Return a callable for the create flow method over gRPC. + + Creates a flow in the specified agent. + + Returns: + Callable[[~.CreateFlowRequest], + ~.Flow]: + 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_flow" not in self._stubs: + self._stubs["create_flow"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/CreateFlow", + request_serializer=gcdc_flow.CreateFlowRequest.serialize, + response_deserializer=gcdc_flow.Flow.deserialize, + ) + return self._stubs["create_flow"] + + @property + def delete_flow(self) -> Callable[[flow.DeleteFlowRequest], empty.Empty]: + r"""Return a callable for the delete flow method over gRPC. + + Deletes a specified flow. + + Returns: + Callable[[~.DeleteFlowRequest], + ~.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_flow" not in self._stubs: + self._stubs["delete_flow"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/DeleteFlow", + request_serializer=flow.DeleteFlowRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_flow"] + + @property + def list_flows(self) -> Callable[[flow.ListFlowsRequest], flow.ListFlowsResponse]: + r"""Return a callable for the list flows method over gRPC. + + Returns the list of all flows in the specified agent. + + Returns: + Callable[[~.ListFlowsRequest], + ~.ListFlowsResponse]: + 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_flows" not in self._stubs: + self._stubs["list_flows"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/ListFlows", + request_serializer=flow.ListFlowsRequest.serialize, + response_deserializer=flow.ListFlowsResponse.deserialize, + ) + return self._stubs["list_flows"] + + @property + def get_flow(self) -> Callable[[flow.GetFlowRequest], flow.Flow]: + r"""Return a callable for the get flow method over gRPC. + + Retrieves the specified flow. + + Returns: + Callable[[~.GetFlowRequest], + ~.Flow]: + 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_flow" not in self._stubs: + self._stubs["get_flow"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/GetFlow", + request_serializer=flow.GetFlowRequest.serialize, + response_deserializer=flow.Flow.deserialize, + ) + return self._stubs["get_flow"] + + @property + def update_flow(self) -> Callable[[gcdc_flow.UpdateFlowRequest], gcdc_flow.Flow]: + r"""Return a callable for the update flow method over gRPC. + + Updates the specified flow. + + Returns: + Callable[[~.UpdateFlowRequest], + ~.Flow]: + 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_flow" not in self._stubs: + self._stubs["update_flow"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/UpdateFlow", + request_serializer=gcdc_flow.UpdateFlowRequest.serialize, + response_deserializer=gcdc_flow.Flow.deserialize, + ) + return self._stubs["update_flow"] + + @property + def train_flow(self) -> Callable[[flow.TrainFlowRequest], operations.Operation]: + r"""Return a callable for the train flow method over gRPC. + + Trains the specified flow. Note that only the flow in + 'draft' environment is trained. + + Returns: + Callable[[~.TrainFlowRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "train_flow" not in self._stubs: + self._stubs["train_flow"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/TrainFlow", + request_serializer=flow.TrainFlowRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["train_flow"] + + +__all__ = ("FlowsGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/flows/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/flows/transports/grpc_asyncio.py new file mode 100644 index 00000000..98ff7b16 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/flows/transports/grpc_asyncio.py @@ -0,0 +1,410 @@ +# -*- 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.api_core import operations_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 grpc.experimental import aio # type: ignore + +from google.cloud.dialogflowcx_v3.types import flow +from google.cloud.dialogflowcx_v3.types import flow as gcdc_flow +from google.longrunning import operations_pb2 as operations # type: ignore +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import FlowsTransport, DEFAULT_CLIENT_INFO +from .grpc import FlowsGrpcTransport + + +class FlowsGrpcAsyncIOTransport(FlowsTransport): + """gRPC AsyncIO backend transport for Flows. + + Service for managing [Flows][google.cloud.dialogflow.cx.v3.Flow]. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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 operations_client(self) -> operations_v1.OperationsAsyncClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if "operations_client" not in self.__dict__: + self.__dict__["operations_client"] = operations_v1.OperationsAsyncClient( + self.grpc_channel + ) + + # Return the client from cache. + return self.__dict__["operations_client"] + + @property + def create_flow( + self, + ) -> Callable[[gcdc_flow.CreateFlowRequest], Awaitable[gcdc_flow.Flow]]: + r"""Return a callable for the create flow method over gRPC. + + Creates a flow in the specified agent. + + Returns: + Callable[[~.CreateFlowRequest], + Awaitable[~.Flow]]: + 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_flow" not in self._stubs: + self._stubs["create_flow"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/CreateFlow", + request_serializer=gcdc_flow.CreateFlowRequest.serialize, + response_deserializer=gcdc_flow.Flow.deserialize, + ) + return self._stubs["create_flow"] + + @property + def delete_flow(self) -> Callable[[flow.DeleteFlowRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete flow method over gRPC. + + Deletes a specified flow. + + Returns: + Callable[[~.DeleteFlowRequest], + 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_flow" not in self._stubs: + self._stubs["delete_flow"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/DeleteFlow", + request_serializer=flow.DeleteFlowRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_flow"] + + @property + def list_flows( + self, + ) -> Callable[[flow.ListFlowsRequest], Awaitable[flow.ListFlowsResponse]]: + r"""Return a callable for the list flows method over gRPC. + + Returns the list of all flows in the specified agent. + + Returns: + Callable[[~.ListFlowsRequest], + Awaitable[~.ListFlowsResponse]]: + 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_flows" not in self._stubs: + self._stubs["list_flows"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/ListFlows", + request_serializer=flow.ListFlowsRequest.serialize, + response_deserializer=flow.ListFlowsResponse.deserialize, + ) + return self._stubs["list_flows"] + + @property + def get_flow(self) -> Callable[[flow.GetFlowRequest], Awaitable[flow.Flow]]: + r"""Return a callable for the get flow method over gRPC. + + Retrieves the specified flow. + + Returns: + Callable[[~.GetFlowRequest], + Awaitable[~.Flow]]: + 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_flow" not in self._stubs: + self._stubs["get_flow"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/GetFlow", + request_serializer=flow.GetFlowRequest.serialize, + response_deserializer=flow.Flow.deserialize, + ) + return self._stubs["get_flow"] + + @property + def update_flow( + self, + ) -> Callable[[gcdc_flow.UpdateFlowRequest], Awaitable[gcdc_flow.Flow]]: + r"""Return a callable for the update flow method over gRPC. + + Updates the specified flow. + + Returns: + Callable[[~.UpdateFlowRequest], + Awaitable[~.Flow]]: + 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_flow" not in self._stubs: + self._stubs["update_flow"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/UpdateFlow", + request_serializer=gcdc_flow.UpdateFlowRequest.serialize, + response_deserializer=gcdc_flow.Flow.deserialize, + ) + return self._stubs["update_flow"] + + @property + def train_flow( + self, + ) -> Callable[[flow.TrainFlowRequest], Awaitable[operations.Operation]]: + r"""Return a callable for the train flow method over gRPC. + + Trains the specified flow. Note that only the flow in + 'draft' environment is trained. + + Returns: + Callable[[~.TrainFlowRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "train_flow" not in self._stubs: + self._stubs["train_flow"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Flows/TrainFlow", + request_serializer=flow.TrainFlowRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["train_flow"] + + +__all__ = ("FlowsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/intents/__init__.py b/google/cloud/dialogflowcx_v3/services/intents/__init__.py new file mode 100644 index 00000000..8719a9b6 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/intents/__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 IntentsClient +from .async_client import IntentsAsyncClient + +__all__ = ( + "IntentsClient", + "IntentsAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/intents/async_client.py b/google/cloud/dialogflowcx_v3/services/intents/async_client.py new file mode 100644 index 00000000..04ee85cf --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/intents/async_client.py @@ -0,0 +1,549 @@ +# -*- 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.dialogflowcx_v3.services.intents import pagers +from google.cloud.dialogflowcx_v3.types import intent +from google.cloud.dialogflowcx_v3.types import intent as gcdc_intent +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import IntentsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import IntentsGrpcAsyncIOTransport +from .client import IntentsClient + + +class IntentsAsyncClient: + """Service for managing + [Intents][google.cloud.dialogflow.cx.v3.Intent]. + """ + + _client: IntentsClient + + DEFAULT_ENDPOINT = IntentsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = IntentsClient.DEFAULT_MTLS_ENDPOINT + + entity_type_path = staticmethod(IntentsClient.entity_type_path) + parse_entity_type_path = staticmethod(IntentsClient.parse_entity_type_path) + intent_path = staticmethod(IntentsClient.intent_path) + parse_intent_path = staticmethod(IntentsClient.parse_intent_path) + + common_billing_account_path = staticmethod( + IntentsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + IntentsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(IntentsClient.common_folder_path) + parse_common_folder_path = staticmethod(IntentsClient.parse_common_folder_path) + + common_organization_path = staticmethod(IntentsClient.common_organization_path) + parse_common_organization_path = staticmethod( + IntentsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(IntentsClient.common_project_path) + parse_common_project_path = staticmethod(IntentsClient.parse_common_project_path) + + common_location_path = staticmethod(IntentsClient.common_location_path) + parse_common_location_path = staticmethod(IntentsClient.parse_common_location_path) + + from_service_account_file = IntentsClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> IntentsTransport: + """Return the transport used by the client instance. + + Returns: + IntentsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(IntentsClient).get_transport_class, type(IntentsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, IntentsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the intents 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, ~.IntentsTransport]): 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 = IntentsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_intents( + self, + request: intent.ListIntentsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListIntentsAsyncPager: + r"""Returns the list of all intents in the specified + agent. + + Args: + request (:class:`~.intent.ListIntentsRequest`): + The request object. The request message for + [Intents.ListIntents][google.cloud.dialogflow.cx.v3.Intents.ListIntents]. + parent (:class:`str`): + Required. The agent to list all intents for. Format: + ``projects//locations//agents/``. + 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: + ~.pagers.ListIntentsAsyncPager: + The response message for + [Intents.ListIntents][google.cloud.dialogflow.cx.v3.Intents.ListIntents]. + + 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 = intent.ListIntentsRequest(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_intents, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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.ListIntentsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_intent( + self, + request: intent.GetIntentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> intent.Intent: + r"""Retrieves the specified intent. + + Args: + request (:class:`~.intent.GetIntentRequest`): + The request object. The request message for + [Intents.GetIntent][google.cloud.dialogflow.cx.v3.Intents.GetIntent]. + name (:class:`str`): + Required. The name of the intent. Format: + ``projects//locations//agents//intents/``. + 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: + ~.intent.Intent: + An intent represents a user's intent + to interact with a conversational agent. + You can provide information for the + Dialogflow API to use to match user + input to an intent by adding training + phrases (i.e., examples of user input) + to your intent. + + """ + # 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 = intent.GetIntentRequest(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_intent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_intent( + self, + request: gcdc_intent.CreateIntentRequest = None, + *, + parent: str = None, + intent: gcdc_intent.Intent = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_intent.Intent: + r"""Creates an intent in the specified agent. + + Args: + request (:class:`~.gcdc_intent.CreateIntentRequest`): + The request object. The request message for + [Intents.CreateIntent][google.cloud.dialogflow.cx.v3.Intents.CreateIntent]. + parent (:class:`str`): + Required. The agent to create an intent for. Format: + ``projects//locations//agents/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + intent (:class:`~.gcdc_intent.Intent`): + Required. The intent to create. + This corresponds to the ``intent`` 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: + ~.gcdc_intent.Intent: + An intent represents a user's intent + to interact with a conversational agent. + You can provide information for the + Dialogflow API to use to match user + input to an intent by adding training + phrases (i.e., examples of user input) + to your intent. + + """ + # 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, intent]) + 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 = gcdc_intent.CreateIntentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if intent is not None: + request.intent = intent + + # 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_intent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_intent( + self, + request: gcdc_intent.UpdateIntentRequest = None, + *, + intent: gcdc_intent.Intent = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_intent.Intent: + r"""Updates the specified intent. + + Args: + request (:class:`~.gcdc_intent.UpdateIntentRequest`): + The request object. The request message for + [Intents.UpdateIntent][google.cloud.dialogflow.cx.v3.Intents.UpdateIntent]. + intent (:class:`~.gcdc_intent.Intent`): + Required. The intent to update. + This corresponds to the ``intent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. If the mask is not present, all + fields will be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_intent.Intent: + An intent represents a user's intent + to interact with a conversational agent. + You can provide information for the + Dialogflow API to use to match user + input to an intent by adding training + phrases (i.e., examples of user input) + to your intent. + + """ + # 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([intent, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = gcdc_intent.UpdateIntentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if intent is not None: + request.intent = intent + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_intent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("intent.name", request.intent.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_intent( + self, + request: intent.DeleteIntentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified intent. + + Args: + request (:class:`~.intent.DeleteIntentRequest`): + The request object. The request message for + [Intents.DeleteIntent][google.cloud.dialogflow.cx.v3.Intents.DeleteIntent]. + name (:class:`str`): + Required. The name of the intent to delete. Format: + ``projects//locations//agents//intents/``. + 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 = intent.DeleteIntentRequest(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_intent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("IntentsAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/intents/client.py b/google/cloud/dialogflowcx_v3/services/intents/client.py new file mode 100644 index 00000000..63d5ef52 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/intents/client.py @@ -0,0 +1,767 @@ +# -*- 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.dialogflowcx_v3.services.intents import pagers +from google.cloud.dialogflowcx_v3.types import intent +from google.cloud.dialogflowcx_v3.types import intent as gcdc_intent +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import IntentsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import IntentsGrpcTransport +from .transports.grpc_asyncio import IntentsGrpcAsyncIOTransport + + +class IntentsClientMeta(type): + """Metaclass for the Intents 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[IntentsTransport]] + _transport_registry["grpc"] = IntentsGrpcTransport + _transport_registry["grpc_asyncio"] = IntentsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[IntentsTransport]: + """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 IntentsClient(metaclass=IntentsClientMeta): + """Service for managing + [Intents][google.cloud.dialogflow.cx.v3.Intent]. + """ + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> IntentsTransport: + """Return the transport used by the client instance. + + Returns: + IntentsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def entity_type_path( + project: str, location: str, agent: str, entity_type: str, + ) -> str: + """Return a fully-qualified entity_type string.""" + return "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + + @staticmethod + def parse_entity_type_path(path: str) -> Dict[str, str]: + """Parse a entity_type path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/entityTypes/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def intent_path(project: str, location: str, agent: str, intent: str,) -> str: + """Return a fully-qualified intent string.""" + return "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + + @staticmethod + def parse_intent_path(path: str) -> Dict[str, str]: + """Parse a intent path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/intents/(?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, IntentsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the intents 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, ~.IntentsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, IntentsTransport): + # transport is a IntentsTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_intents( + self, + request: intent.ListIntentsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListIntentsPager: + r"""Returns the list of all intents in the specified + agent. + + Args: + request (:class:`~.intent.ListIntentsRequest`): + The request object. The request message for + [Intents.ListIntents][google.cloud.dialogflow.cx.v3.Intents.ListIntents]. + parent (:class:`str`): + Required. The agent to list all intents for. Format: + ``projects//locations//agents/``. + 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: + ~.pagers.ListIntentsPager: + The response message for + [Intents.ListIntents][google.cloud.dialogflow.cx.v3.Intents.ListIntents]. + + 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 intent.ListIntentsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, intent.ListIntentsRequest): + request = intent.ListIntentsRequest(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_intents] + + # 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.ListIntentsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_intent( + self, + request: intent.GetIntentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> intent.Intent: + r"""Retrieves the specified intent. + + Args: + request (:class:`~.intent.GetIntentRequest`): + The request object. The request message for + [Intents.GetIntent][google.cloud.dialogflow.cx.v3.Intents.GetIntent]. + name (:class:`str`): + Required. The name of the intent. Format: + ``projects//locations//agents//intents/``. + 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: + ~.intent.Intent: + An intent represents a user's intent + to interact with a conversational agent. + You can provide information for the + Dialogflow API to use to match user + input to an intent by adding training + phrases (i.e., examples of user input) + to your intent. + + """ + # 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 intent.GetIntentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, intent.GetIntentRequest): + request = intent.GetIntentRequest(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_intent] + + # 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_intent( + self, + request: gcdc_intent.CreateIntentRequest = None, + *, + parent: str = None, + intent: gcdc_intent.Intent = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_intent.Intent: + r"""Creates an intent in the specified agent. + + Args: + request (:class:`~.gcdc_intent.CreateIntentRequest`): + The request object. The request message for + [Intents.CreateIntent][google.cloud.dialogflow.cx.v3.Intents.CreateIntent]. + parent (:class:`str`): + Required. The agent to create an intent for. Format: + ``projects//locations//agents/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + intent (:class:`~.gcdc_intent.Intent`): + Required. The intent to create. + This corresponds to the ``intent`` 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: + ~.gcdc_intent.Intent: + An intent represents a user's intent + to interact with a conversational agent. + You can provide information for the + Dialogflow API to use to match user + input to an intent by adding training + phrases (i.e., examples of user input) + to your intent. + + """ + # 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, intent]) + 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 gcdc_intent.CreateIntentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_intent.CreateIntentRequest): + request = gcdc_intent.CreateIntentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if intent is not None: + request.intent = intent + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_intent] + + # 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_intent( + self, + request: gcdc_intent.UpdateIntentRequest = None, + *, + intent: gcdc_intent.Intent = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_intent.Intent: + r"""Updates the specified intent. + + Args: + request (:class:`~.gcdc_intent.UpdateIntentRequest`): + The request object. The request message for + [Intents.UpdateIntent][google.cloud.dialogflow.cx.v3.Intents.UpdateIntent]. + intent (:class:`~.gcdc_intent.Intent`): + Required. The intent to update. + This corresponds to the ``intent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. If the mask is not present, all + fields will be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_intent.Intent: + An intent represents a user's intent + to interact with a conversational agent. + You can provide information for the + Dialogflow API to use to match user + input to an intent by adding training + phrases (i.e., examples of user input) + to your intent. + + """ + # 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([intent, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a gcdc_intent.UpdateIntentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_intent.UpdateIntentRequest): + request = gcdc_intent.UpdateIntentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if intent is not None: + request.intent = intent + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_intent] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("intent.name", request.intent.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def delete_intent( + self, + request: intent.DeleteIntentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified intent. + + Args: + request (:class:`~.intent.DeleteIntentRequest`): + The request object. The request message for + [Intents.DeleteIntent][google.cloud.dialogflow.cx.v3.Intents.DeleteIntent]. + name (:class:`str`): + Required. The name of the intent to delete. Format: + ``projects//locations//agents//intents/``. + 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 intent.DeleteIntentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, intent.DeleteIntentRequest): + request = intent.DeleteIntentRequest(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_intent] + + # 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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("IntentsClient",) diff --git a/google/cloud/dialogflowcx_v3/services/intents/pagers.py b/google/cloud/dialogflowcx_v3/services/intents/pagers.py new file mode 100644 index 00000000..1e3f29dd --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/intents/pagers.py @@ -0,0 +1,148 @@ +# -*- 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 + +from google.cloud.dialogflowcx_v3.types import intent + + +class ListIntentsPager: + """A pager for iterating through ``list_intents`` requests. + + This class thinly wraps an initial + :class:`~.intent.ListIntentsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``intents`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListIntents`` requests and continue to iterate + through the ``intents`` field on the + corresponding responses. + + All the usual :class:`~.intent.ListIntentsResponse` + 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[..., intent.ListIntentsResponse], + request: intent.ListIntentsRequest, + response: intent.ListIntentsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.intent.ListIntentsRequest`): + The initial request object. + response (:class:`~.intent.ListIntentsResponse`): + 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 = intent.ListIntentsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[intent.ListIntentsResponse]: + 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[intent.Intent]: + for page in self.pages: + yield from page.intents + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListIntentsAsyncPager: + """A pager for iterating through ``list_intents`` requests. + + This class thinly wraps an initial + :class:`~.intent.ListIntentsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``intents`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListIntents`` requests and continue to iterate + through the ``intents`` field on the + corresponding responses. + + All the usual :class:`~.intent.ListIntentsResponse` + 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[intent.ListIntentsResponse]], + request: intent.ListIntentsRequest, + response: intent.ListIntentsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.intent.ListIntentsRequest`): + The initial request object. + response (:class:`~.intent.ListIntentsResponse`): + 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 = intent.ListIntentsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[intent.ListIntentsResponse]: + 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[intent.Intent]: + async def async_generator(): + async for page in self.pages: + for response in page.intents: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflowcx_v3/services/intents/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/intents/transports/__init__.py new file mode 100644 index 00000000..ecbfad6b --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/intents/transports/__init__.py @@ -0,0 +1,36 @@ +# -*- 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 IntentsTransport +from .grpc import IntentsGrpcTransport +from .grpc_asyncio import IntentsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[IntentsTransport]] +_transport_registry["grpc"] = IntentsGrpcTransport +_transport_registry["grpc_asyncio"] = IntentsGrpcAsyncIOTransport + + +__all__ = ( + "IntentsTransport", + "IntentsGrpcTransport", + "IntentsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/intents/transports/base.py b/google/cloud/dialogflowcx_v3/services/intents/transports/base.py new file mode 100644 index 00000000..64e82a1e --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/intents/transports/base.py @@ -0,0 +1,179 @@ +# -*- 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.dialogflowcx_v3.types import intent +from google.cloud.dialogflowcx_v3.types import intent as gcdc_intent +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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class IntentsTransport(abc.ABC): + """Abstract transport class for Intents.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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_intents: gapic_v1.method.wrap_method( + self.list_intents, default_timeout=None, client_info=client_info, + ), + self.get_intent: gapic_v1.method.wrap_method( + self.get_intent, default_timeout=None, client_info=client_info, + ), + self.create_intent: gapic_v1.method.wrap_method( + self.create_intent, default_timeout=None, client_info=client_info, + ), + self.update_intent: gapic_v1.method.wrap_method( + self.update_intent, default_timeout=None, client_info=client_info, + ), + self.delete_intent: gapic_v1.method.wrap_method( + self.delete_intent, default_timeout=None, client_info=client_info, + ), + } + + @property + def list_intents( + self, + ) -> typing.Callable[ + [intent.ListIntentsRequest], + typing.Union[ + intent.ListIntentsResponse, typing.Awaitable[intent.ListIntentsResponse] + ], + ]: + raise NotImplementedError() + + @property + def get_intent( + self, + ) -> typing.Callable[ + [intent.GetIntentRequest], + typing.Union[intent.Intent, typing.Awaitable[intent.Intent]], + ]: + raise NotImplementedError() + + @property + def create_intent( + self, + ) -> typing.Callable[ + [gcdc_intent.CreateIntentRequest], + typing.Union[gcdc_intent.Intent, typing.Awaitable[gcdc_intent.Intent]], + ]: + raise NotImplementedError() + + @property + def update_intent( + self, + ) -> typing.Callable[ + [gcdc_intent.UpdateIntentRequest], + typing.Union[gcdc_intent.Intent, typing.Awaitable[gcdc_intent.Intent]], + ]: + raise NotImplementedError() + + @property + def delete_intent( + self, + ) -> typing.Callable[ + [intent.DeleteIntentRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + +__all__ = ("IntentsTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/intents/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/intents/transports/grpc.py new file mode 100644 index 00000000..41b2a08c --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/intents/transports/grpc.py @@ -0,0 +1,363 @@ +# -*- 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.dialogflowcx_v3.types import intent +from google.cloud.dialogflowcx_v3.types import intent as gcdc_intent +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import IntentsTransport, DEFAULT_CLIENT_INFO + + +class IntentsGrpcTransport(IntentsTransport): + """gRPC backend transport for Intents. + + Service for managing + [Intents][google.cloud.dialogflow.cx.v3.Intent]. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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_intents( + self, + ) -> Callable[[intent.ListIntentsRequest], intent.ListIntentsResponse]: + r"""Return a callable for the list intents method over gRPC. + + Returns the list of all intents in the specified + agent. + + Returns: + Callable[[~.ListIntentsRequest], + ~.ListIntentsResponse]: + 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_intents" not in self._stubs: + self._stubs["list_intents"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Intents/ListIntents", + request_serializer=intent.ListIntentsRequest.serialize, + response_deserializer=intent.ListIntentsResponse.deserialize, + ) + return self._stubs["list_intents"] + + @property + def get_intent(self) -> Callable[[intent.GetIntentRequest], intent.Intent]: + r"""Return a callable for the get intent method over gRPC. + + Retrieves the specified intent. + + Returns: + Callable[[~.GetIntentRequest], + ~.Intent]: + 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_intent" not in self._stubs: + self._stubs["get_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Intents/GetIntent", + request_serializer=intent.GetIntentRequest.serialize, + response_deserializer=intent.Intent.deserialize, + ) + return self._stubs["get_intent"] + + @property + def create_intent( + self, + ) -> Callable[[gcdc_intent.CreateIntentRequest], gcdc_intent.Intent]: + r"""Return a callable for the create intent method over gRPC. + + Creates an intent in the specified agent. + + Returns: + Callable[[~.CreateIntentRequest], + ~.Intent]: + 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_intent" not in self._stubs: + self._stubs["create_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Intents/CreateIntent", + request_serializer=gcdc_intent.CreateIntentRequest.serialize, + response_deserializer=gcdc_intent.Intent.deserialize, + ) + return self._stubs["create_intent"] + + @property + def update_intent( + self, + ) -> Callable[[gcdc_intent.UpdateIntentRequest], gcdc_intent.Intent]: + r"""Return a callable for the update intent method over gRPC. + + Updates the specified intent. + + Returns: + Callable[[~.UpdateIntentRequest], + ~.Intent]: + 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_intent" not in self._stubs: + self._stubs["update_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Intents/UpdateIntent", + request_serializer=gcdc_intent.UpdateIntentRequest.serialize, + response_deserializer=gcdc_intent.Intent.deserialize, + ) + return self._stubs["update_intent"] + + @property + def delete_intent(self) -> Callable[[intent.DeleteIntentRequest], empty.Empty]: + r"""Return a callable for the delete intent method over gRPC. + + Deletes the specified intent. + + Returns: + Callable[[~.DeleteIntentRequest], + ~.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_intent" not in self._stubs: + self._stubs["delete_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Intents/DeleteIntent", + request_serializer=intent.DeleteIntentRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_intent"] + + +__all__ = ("IntentsGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/intents/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/intents/transports/grpc_asyncio.py new file mode 100644 index 00000000..a0b3dd56 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/intents/transports/grpc_asyncio.py @@ -0,0 +1,371 @@ +# -*- 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.dialogflowcx_v3.types import intent +from google.cloud.dialogflowcx_v3.types import intent as gcdc_intent +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import IntentsTransport, DEFAULT_CLIENT_INFO +from .grpc import IntentsGrpcTransport + + +class IntentsGrpcAsyncIOTransport(IntentsTransport): + """gRPC AsyncIO backend transport for Intents. + + Service for managing + [Intents][google.cloud.dialogflow.cx.v3.Intent]. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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_intents( + self, + ) -> Callable[[intent.ListIntentsRequest], Awaitable[intent.ListIntentsResponse]]: + r"""Return a callable for the list intents method over gRPC. + + Returns the list of all intents in the specified + agent. + + Returns: + Callable[[~.ListIntentsRequest], + Awaitable[~.ListIntentsResponse]]: + 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_intents" not in self._stubs: + self._stubs["list_intents"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Intents/ListIntents", + request_serializer=intent.ListIntentsRequest.serialize, + response_deserializer=intent.ListIntentsResponse.deserialize, + ) + return self._stubs["list_intents"] + + @property + def get_intent( + self, + ) -> Callable[[intent.GetIntentRequest], Awaitable[intent.Intent]]: + r"""Return a callable for the get intent method over gRPC. + + Retrieves the specified intent. + + Returns: + Callable[[~.GetIntentRequest], + Awaitable[~.Intent]]: + 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_intent" not in self._stubs: + self._stubs["get_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Intents/GetIntent", + request_serializer=intent.GetIntentRequest.serialize, + response_deserializer=intent.Intent.deserialize, + ) + return self._stubs["get_intent"] + + @property + def create_intent( + self, + ) -> Callable[[gcdc_intent.CreateIntentRequest], Awaitable[gcdc_intent.Intent]]: + r"""Return a callable for the create intent method over gRPC. + + Creates an intent in the specified agent. + + Returns: + Callable[[~.CreateIntentRequest], + Awaitable[~.Intent]]: + 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_intent" not in self._stubs: + self._stubs["create_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Intents/CreateIntent", + request_serializer=gcdc_intent.CreateIntentRequest.serialize, + response_deserializer=gcdc_intent.Intent.deserialize, + ) + return self._stubs["create_intent"] + + @property + def update_intent( + self, + ) -> Callable[[gcdc_intent.UpdateIntentRequest], Awaitable[gcdc_intent.Intent]]: + r"""Return a callable for the update intent method over gRPC. + + Updates the specified intent. + + Returns: + Callable[[~.UpdateIntentRequest], + Awaitable[~.Intent]]: + 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_intent" not in self._stubs: + self._stubs["update_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Intents/UpdateIntent", + request_serializer=gcdc_intent.UpdateIntentRequest.serialize, + response_deserializer=gcdc_intent.Intent.deserialize, + ) + return self._stubs["update_intent"] + + @property + def delete_intent( + self, + ) -> Callable[[intent.DeleteIntentRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete intent method over gRPC. + + Deletes the specified intent. + + Returns: + Callable[[~.DeleteIntentRequest], + 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_intent" not in self._stubs: + self._stubs["delete_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Intents/DeleteIntent", + request_serializer=intent.DeleteIntentRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_intent"] + + +__all__ = ("IntentsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/pages/__init__.py b/google/cloud/dialogflowcx_v3/services/pages/__init__.py new file mode 100644 index 00000000..4fff3f52 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/pages/__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 PagesClient +from .async_client import PagesAsyncClient + +__all__ = ( + "PagesClient", + "PagesAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/pages/async_client.py b/google/cloud/dialogflowcx_v3/services/pages/async_client.py new file mode 100644 index 00000000..3a900c6f --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/pages/async_client.py @@ -0,0 +1,597 @@ +# -*- 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.dialogflowcx_v3.services.pages import pagers +from google.cloud.dialogflowcx_v3.types import fulfillment +from google.cloud.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import page as gcdc_page +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import PagesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import PagesGrpcAsyncIOTransport +from .client import PagesClient + + +class PagesAsyncClient: + """Service for managing [Pages][google.cloud.dialogflow.cx.v3.Page].""" + + _client: PagesClient + + DEFAULT_ENDPOINT = PagesClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = PagesClient.DEFAULT_MTLS_ENDPOINT + + entity_type_path = staticmethod(PagesClient.entity_type_path) + parse_entity_type_path = staticmethod(PagesClient.parse_entity_type_path) + flow_path = staticmethod(PagesClient.flow_path) + parse_flow_path = staticmethod(PagesClient.parse_flow_path) + intent_path = staticmethod(PagesClient.intent_path) + parse_intent_path = staticmethod(PagesClient.parse_intent_path) + page_path = staticmethod(PagesClient.page_path) + parse_page_path = staticmethod(PagesClient.parse_page_path) + transition_route_group_path = staticmethod(PagesClient.transition_route_group_path) + parse_transition_route_group_path = staticmethod( + PagesClient.parse_transition_route_group_path + ) + webhook_path = staticmethod(PagesClient.webhook_path) + parse_webhook_path = staticmethod(PagesClient.parse_webhook_path) + + common_billing_account_path = staticmethod(PagesClient.common_billing_account_path) + parse_common_billing_account_path = staticmethod( + PagesClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(PagesClient.common_folder_path) + parse_common_folder_path = staticmethod(PagesClient.parse_common_folder_path) + + common_organization_path = staticmethod(PagesClient.common_organization_path) + parse_common_organization_path = staticmethod( + PagesClient.parse_common_organization_path + ) + + common_project_path = staticmethod(PagesClient.common_project_path) + parse_common_project_path = staticmethod(PagesClient.parse_common_project_path) + + common_location_path = staticmethod(PagesClient.common_location_path) + parse_common_location_path = staticmethod(PagesClient.parse_common_location_path) + + from_service_account_file = PagesClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> PagesTransport: + """Return the transport used by the client instance. + + Returns: + PagesTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(PagesClient).get_transport_class, type(PagesClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, PagesTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the pages 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, ~.PagesTransport]): 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 = PagesClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_pages( + self, + request: page.ListPagesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListPagesAsyncPager: + r"""Returns the list of all pages in the specified flow. + + Args: + request (:class:`~.page.ListPagesRequest`): + The request object. The request message for + [Pages.ListPages][google.cloud.dialogflow.cx.v3.Pages.ListPages]. + parent (:class:`str`): + Required. The flow to list all pages for. Format: + ``projects//locations//agents//flows/``. + 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: + ~.pagers.ListPagesAsyncPager: + The response message for + [Pages.ListPages][google.cloud.dialogflow.cx.v3.Pages.ListPages]. + + 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 = page.ListPagesRequest(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_pages, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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.ListPagesAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_page( + self, + request: page.GetPageRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> page.Page: + r"""Retrieves the specified page. + + Args: + request (:class:`~.page.GetPageRequest`): + The request object. The request message for + [Pages.GetPage][google.cloud.dialogflow.cx.v3.Pages.GetPage]. + name (:class:`str`): + Required. The name of the page. Format: + ``projects//locations//agents//flows//pages/``. + 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: + ~.page.Page: + A Dialogflow CX conversation (session) can be described + and visualized as a state machine. The states of a CX + session are represented by pages. + + For each flow, you define many pages, where your + combined pages can handle a complete conversation on the + topics the flow is designed for. At any given moment, + exactly one page is the current page, the current page + is considered active, and the flow associated with that + page is considered active. Every flow has a special + start page. When a flow initially becomes active, the + start page page becomes the current page. For each + conversational turn, the current page will either stay + the same or transition to another page. + + You configure each page to collect information from the + end-user that is relevant for the conversational state + represented by the page. + + For more information, see the `Page + guide `__. + + """ + # 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 = page.GetPageRequest(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_page, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_page( + self, + request: gcdc_page.CreatePageRequest = None, + *, + parent: str = None, + page: gcdc_page.Page = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_page.Page: + r"""Creates a page in the specified flow. + + Args: + request (:class:`~.gcdc_page.CreatePageRequest`): + The request object. The request message for + [Pages.CreatePage][google.cloud.dialogflow.cx.v3.Pages.CreatePage]. + parent (:class:`str`): + Required. The flow to create a page for. Format: + ``projects//locations//agents//flows/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + page (:class:`~.gcdc_page.Page`): + Required. The page to create. + This corresponds to the ``page`` 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: + ~.gcdc_page.Page: + A Dialogflow CX conversation (session) can be described + and visualized as a state machine. The states of a CX + session are represented by pages. + + For each flow, you define many pages, where your + combined pages can handle a complete conversation on the + topics the flow is designed for. At any given moment, + exactly one page is the current page, the current page + is considered active, and the flow associated with that + page is considered active. Every flow has a special + start page. When a flow initially becomes active, the + start page page becomes the current page. For each + conversational turn, the current page will either stay + the same or transition to another page. + + You configure each page to collect information from the + end-user that is relevant for the conversational state + represented by the page. + + For more information, see the `Page + guide `__. + + """ + # 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, page]) + 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 = gcdc_page.CreatePageRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if page is not None: + request.page = page + + # 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_page, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_page( + self, + request: gcdc_page.UpdatePageRequest = None, + *, + page: gcdc_page.Page = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_page.Page: + r"""Updates the specified page. + + Args: + request (:class:`~.gcdc_page.UpdatePageRequest`): + The request object. The request message for + [Pages.UpdatePage][google.cloud.dialogflow.cx.v3.Pages.UpdatePage]. + page (:class:`~.gcdc_page.Page`): + Required. The page to update. + This corresponds to the ``page`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. If the mask is not present, all + fields will be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_page.Page: + A Dialogflow CX conversation (session) can be described + and visualized as a state machine. The states of a CX + session are represented by pages. + + For each flow, you define many pages, where your + combined pages can handle a complete conversation on the + topics the flow is designed for. At any given moment, + exactly one page is the current page, the current page + is considered active, and the flow associated with that + page is considered active. Every flow has a special + start page. When a flow initially becomes active, the + start page page becomes the current page. For each + conversational turn, the current page will either stay + the same or transition to another page. + + You configure each page to collect information from the + end-user that is relevant for the conversational state + represented by the page. + + For more information, see the `Page + guide `__. + + """ + # 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([page, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = gcdc_page.UpdatePageRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if page is not None: + request.page = page + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_page, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("page.name", request.page.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_page( + self, + request: page.DeletePageRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified page. + + Args: + request (:class:`~.page.DeletePageRequest`): + The request object. The request message for + [Pages.DeletePage][google.cloud.dialogflow.cx.v3.Pages.DeletePage]. + name (:class:`str`): + Required. The name of the page to delete. Format: + ``projects//locations//agents//Flows//pages/``. + 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 = page.DeletePageRequest(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_page, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("PagesAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/pages/client.py b/google/cloud/dialogflowcx_v3/services/pages/client.py new file mode 100644 index 00000000..3a5c376f --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/pages/client.py @@ -0,0 +1,879 @@ +# -*- 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.dialogflowcx_v3.services.pages import pagers +from google.cloud.dialogflowcx_v3.types import fulfillment +from google.cloud.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import page as gcdc_page +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import PagesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import PagesGrpcTransport +from .transports.grpc_asyncio import PagesGrpcAsyncIOTransport + + +class PagesClientMeta(type): + """Metaclass for the Pages 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[PagesTransport]] + _transport_registry["grpc"] = PagesGrpcTransport + _transport_registry["grpc_asyncio"] = PagesGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[PagesTransport]: + """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 PagesClient(metaclass=PagesClientMeta): + """Service for managing [Pages][google.cloud.dialogflow.cx.v3.Page].""" + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> PagesTransport: + """Return the transport used by the client instance. + + Returns: + PagesTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def entity_type_path( + project: str, location: str, agent: str, entity_type: str, + ) -> str: + """Return a fully-qualified entity_type string.""" + return "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + + @staticmethod + def parse_entity_type_path(path: str) -> Dict[str, str]: + """Parse a entity_type path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/entityTypes/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def flow_path(project: str, location: str, agent: str, flow: str,) -> str: + """Return a fully-qualified flow string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + + @staticmethod + def parse_flow_path(path: str) -> Dict[str, str]: + """Parse a flow path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def intent_path(project: str, location: str, agent: str, intent: str,) -> str: + """Return a fully-qualified intent string.""" + return "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + + @staticmethod + def parse_intent_path(path: str) -> Dict[str, str]: + """Parse a intent path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/intents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def page_path( + project: str, location: str, agent: str, flow: str, page: str, + ) -> str: + """Return a fully-qualified page string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + + @staticmethod + def parse_page_path(path: str) -> Dict[str, str]: + """Parse a page path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/pages/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def transition_route_group_path( + project: str, location: str, agent: str, flow: str, transition_route_group: str, + ) -> str: + """Return a fully-qualified transition_route_group string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/transitionRouteGroups/{transition_route_group}".format( + project=project, + location=location, + agent=agent, + flow=flow, + transition_route_group=transition_route_group, + ) + + @staticmethod + def parse_transition_route_group_path(path: str) -> Dict[str, str]: + """Parse a transition_route_group path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/transitionRouteGroups/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def webhook_path(project: str, location: str, agent: str, webhook: str,) -> str: + """Return a fully-qualified webhook string.""" + return "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + + @staticmethod + def parse_webhook_path(path: str) -> Dict[str, str]: + """Parse a webhook path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/webhooks/(?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, PagesTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the pages 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, ~.PagesTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, PagesTransport): + # transport is a PagesTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_pages( + self, + request: page.ListPagesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListPagesPager: + r"""Returns the list of all pages in the specified flow. + + Args: + request (:class:`~.page.ListPagesRequest`): + The request object. The request message for + [Pages.ListPages][google.cloud.dialogflow.cx.v3.Pages.ListPages]. + parent (:class:`str`): + Required. The flow to list all pages for. Format: + ``projects//locations//agents//flows/``. + 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: + ~.pagers.ListPagesPager: + The response message for + [Pages.ListPages][google.cloud.dialogflow.cx.v3.Pages.ListPages]. + + 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 page.ListPagesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, page.ListPagesRequest): + request = page.ListPagesRequest(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_pages] + + # 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.ListPagesPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_page( + self, + request: page.GetPageRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> page.Page: + r"""Retrieves the specified page. + + Args: + request (:class:`~.page.GetPageRequest`): + The request object. The request message for + [Pages.GetPage][google.cloud.dialogflow.cx.v3.Pages.GetPage]. + name (:class:`str`): + Required. The name of the page. Format: + ``projects//locations//agents//flows//pages/``. + 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: + ~.page.Page: + A Dialogflow CX conversation (session) can be described + and visualized as a state machine. The states of a CX + session are represented by pages. + + For each flow, you define many pages, where your + combined pages can handle a complete conversation on the + topics the flow is designed for. At any given moment, + exactly one page is the current page, the current page + is considered active, and the flow associated with that + page is considered active. Every flow has a special + start page. When a flow initially becomes active, the + start page page becomes the current page. For each + conversational turn, the current page will either stay + the same or transition to another page. + + You configure each page to collect information from the + end-user that is relevant for the conversational state + represented by the page. + + For more information, see the `Page + guide `__. + + """ + # 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 page.GetPageRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, page.GetPageRequest): + request = page.GetPageRequest(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_page] + + # 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_page( + self, + request: gcdc_page.CreatePageRequest = None, + *, + parent: str = None, + page: gcdc_page.Page = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_page.Page: + r"""Creates a page in the specified flow. + + Args: + request (:class:`~.gcdc_page.CreatePageRequest`): + The request object. The request message for + [Pages.CreatePage][google.cloud.dialogflow.cx.v3.Pages.CreatePage]. + parent (:class:`str`): + Required. The flow to create a page for. Format: + ``projects//locations//agents//flows/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + page (:class:`~.gcdc_page.Page`): + Required. The page to create. + This corresponds to the ``page`` 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: + ~.gcdc_page.Page: + A Dialogflow CX conversation (session) can be described + and visualized as a state machine. The states of a CX + session are represented by pages. + + For each flow, you define many pages, where your + combined pages can handle a complete conversation on the + topics the flow is designed for. At any given moment, + exactly one page is the current page, the current page + is considered active, and the flow associated with that + page is considered active. Every flow has a special + start page. When a flow initially becomes active, the + start page page becomes the current page. For each + conversational turn, the current page will either stay + the same or transition to another page. + + You configure each page to collect information from the + end-user that is relevant for the conversational state + represented by the page. + + For more information, see the `Page + guide `__. + + """ + # 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, page]) + 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 gcdc_page.CreatePageRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_page.CreatePageRequest): + request = gcdc_page.CreatePageRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if page is not None: + request.page = page + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_page] + + # 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_page( + self, + request: gcdc_page.UpdatePageRequest = None, + *, + page: gcdc_page.Page = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_page.Page: + r"""Updates the specified page. + + Args: + request (:class:`~.gcdc_page.UpdatePageRequest`): + The request object. The request message for + [Pages.UpdatePage][google.cloud.dialogflow.cx.v3.Pages.UpdatePage]. + page (:class:`~.gcdc_page.Page`): + Required. The page to update. + This corresponds to the ``page`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. If the mask is not present, all + fields will be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_page.Page: + A Dialogflow CX conversation (session) can be described + and visualized as a state machine. The states of a CX + session are represented by pages. + + For each flow, you define many pages, where your + combined pages can handle a complete conversation on the + topics the flow is designed for. At any given moment, + exactly one page is the current page, the current page + is considered active, and the flow associated with that + page is considered active. Every flow has a special + start page. When a flow initially becomes active, the + start page page becomes the current page. For each + conversational turn, the current page will either stay + the same or transition to another page. + + You configure each page to collect information from the + end-user that is relevant for the conversational state + represented by the page. + + For more information, see the `Page + guide `__. + + """ + # 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([page, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a gcdc_page.UpdatePageRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_page.UpdatePageRequest): + request = gcdc_page.UpdatePageRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if page is not None: + request.page = page + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_page] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("page.name", request.page.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def delete_page( + self, + request: page.DeletePageRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified page. + + Args: + request (:class:`~.page.DeletePageRequest`): + The request object. The request message for + [Pages.DeletePage][google.cloud.dialogflow.cx.v3.Pages.DeletePage]. + name (:class:`str`): + Required. The name of the page to delete. Format: + ``projects//locations//agents//Flows//pages/``. + 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 page.DeletePageRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, page.DeletePageRequest): + request = page.DeletePageRequest(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_page] + + # 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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("PagesClient",) diff --git a/google/cloud/dialogflowcx_v3/services/pages/pagers.py b/google/cloud/dialogflowcx_v3/services/pages/pagers.py new file mode 100644 index 00000000..40d758e7 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/pages/pagers.py @@ -0,0 +1,148 @@ +# -*- 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 + +from google.cloud.dialogflowcx_v3.types import page + + +class ListPagesPager: + """A pager for iterating through ``list_pages`` requests. + + This class thinly wraps an initial + :class:`~.page.ListPagesResponse` object, and + provides an ``__iter__`` method to iterate through its + ``pages`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListPages`` requests and continue to iterate + through the ``pages`` field on the + corresponding responses. + + All the usual :class:`~.page.ListPagesResponse` + 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[..., page.ListPagesResponse], + request: page.ListPagesRequest, + response: page.ListPagesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.page.ListPagesRequest`): + The initial request object. + response (:class:`~.page.ListPagesResponse`): + 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 = page.ListPagesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[page.ListPagesResponse]: + 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[page.Page]: + for page in self.pages: + yield from page.pages + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListPagesAsyncPager: + """A pager for iterating through ``list_pages`` requests. + + This class thinly wraps an initial + :class:`~.page.ListPagesResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``pages`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListPages`` requests and continue to iterate + through the ``pages`` field on the + corresponding responses. + + All the usual :class:`~.page.ListPagesResponse` + 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[page.ListPagesResponse]], + request: page.ListPagesRequest, + response: page.ListPagesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.page.ListPagesRequest`): + The initial request object. + response (:class:`~.page.ListPagesResponse`): + 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 = page.ListPagesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[page.ListPagesResponse]: + 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[page.Page]: + async def async_generator(): + async for page in self.pages: + for response in page.pages: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflowcx_v3/services/pages/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/pages/transports/__init__.py new file mode 100644 index 00000000..ca4547fb --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/pages/transports/__init__.py @@ -0,0 +1,36 @@ +# -*- 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 PagesTransport +from .grpc import PagesGrpcTransport +from .grpc_asyncio import PagesGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[PagesTransport]] +_transport_registry["grpc"] = PagesGrpcTransport +_transport_registry["grpc_asyncio"] = PagesGrpcAsyncIOTransport + + +__all__ = ( + "PagesTransport", + "PagesGrpcTransport", + "PagesGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/pages/transports/base.py b/google/cloud/dialogflowcx_v3/services/pages/transports/base.py new file mode 100644 index 00000000..ce7f72f8 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/pages/transports/base.py @@ -0,0 +1,176 @@ +# -*- 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.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import page as gcdc_page +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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class PagesTransport(abc.ABC): + """Abstract transport class for Pages.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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_pages: gapic_v1.method.wrap_method( + self.list_pages, default_timeout=None, client_info=client_info, + ), + self.get_page: gapic_v1.method.wrap_method( + self.get_page, default_timeout=None, client_info=client_info, + ), + self.create_page: gapic_v1.method.wrap_method( + self.create_page, default_timeout=None, client_info=client_info, + ), + self.update_page: gapic_v1.method.wrap_method( + self.update_page, default_timeout=None, client_info=client_info, + ), + self.delete_page: gapic_v1.method.wrap_method( + self.delete_page, default_timeout=None, client_info=client_info, + ), + } + + @property + def list_pages( + self, + ) -> typing.Callable[ + [page.ListPagesRequest], + typing.Union[page.ListPagesResponse, typing.Awaitable[page.ListPagesResponse]], + ]: + raise NotImplementedError() + + @property + def get_page( + self, + ) -> typing.Callable[ + [page.GetPageRequest], typing.Union[page.Page, typing.Awaitable[page.Page]] + ]: + raise NotImplementedError() + + @property + def create_page( + self, + ) -> typing.Callable[ + [gcdc_page.CreatePageRequest], + typing.Union[gcdc_page.Page, typing.Awaitable[gcdc_page.Page]], + ]: + raise NotImplementedError() + + @property + def update_page( + self, + ) -> typing.Callable[ + [gcdc_page.UpdatePageRequest], + typing.Union[gcdc_page.Page, typing.Awaitable[gcdc_page.Page]], + ]: + raise NotImplementedError() + + @property + def delete_page( + self, + ) -> typing.Callable[ + [page.DeletePageRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + +__all__ = ("PagesTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/pages/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/pages/transports/grpc.py new file mode 100644 index 00000000..773599fd --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/pages/transports/grpc.py @@ -0,0 +1,355 @@ +# -*- 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.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import page as gcdc_page +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import PagesTransport, DEFAULT_CLIENT_INFO + + +class PagesGrpcTransport(PagesTransport): + """gRPC backend transport for Pages. + + Service for managing [Pages][google.cloud.dialogflow.cx.v3.Page]. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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_pages(self) -> Callable[[page.ListPagesRequest], page.ListPagesResponse]: + r"""Return a callable for the list pages method over gRPC. + + Returns the list of all pages in the specified flow. + + Returns: + Callable[[~.ListPagesRequest], + ~.ListPagesResponse]: + 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_pages" not in self._stubs: + self._stubs["list_pages"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Pages/ListPages", + request_serializer=page.ListPagesRequest.serialize, + response_deserializer=page.ListPagesResponse.deserialize, + ) + return self._stubs["list_pages"] + + @property + def get_page(self) -> Callable[[page.GetPageRequest], page.Page]: + r"""Return a callable for the get page method over gRPC. + + Retrieves the specified page. + + Returns: + Callable[[~.GetPageRequest], + ~.Page]: + 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_page" not in self._stubs: + self._stubs["get_page"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Pages/GetPage", + request_serializer=page.GetPageRequest.serialize, + response_deserializer=page.Page.deserialize, + ) + return self._stubs["get_page"] + + @property + def create_page(self) -> Callable[[gcdc_page.CreatePageRequest], gcdc_page.Page]: + r"""Return a callable for the create page method over gRPC. + + Creates a page in the specified flow. + + Returns: + Callable[[~.CreatePageRequest], + ~.Page]: + 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_page" not in self._stubs: + self._stubs["create_page"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Pages/CreatePage", + request_serializer=gcdc_page.CreatePageRequest.serialize, + response_deserializer=gcdc_page.Page.deserialize, + ) + return self._stubs["create_page"] + + @property + def update_page(self) -> Callable[[gcdc_page.UpdatePageRequest], gcdc_page.Page]: + r"""Return a callable for the update page method over gRPC. + + Updates the specified page. + + Returns: + Callable[[~.UpdatePageRequest], + ~.Page]: + 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_page" not in self._stubs: + self._stubs["update_page"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Pages/UpdatePage", + request_serializer=gcdc_page.UpdatePageRequest.serialize, + response_deserializer=gcdc_page.Page.deserialize, + ) + return self._stubs["update_page"] + + @property + def delete_page(self) -> Callable[[page.DeletePageRequest], empty.Empty]: + r"""Return a callable for the delete page method over gRPC. + + Deletes the specified page. + + Returns: + Callable[[~.DeletePageRequest], + ~.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_page" not in self._stubs: + self._stubs["delete_page"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Pages/DeletePage", + request_serializer=page.DeletePageRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_page"] + + +__all__ = ("PagesGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/pages/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/pages/transports/grpc_asyncio.py new file mode 100644 index 00000000..b1a41072 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/pages/transports/grpc_asyncio.py @@ -0,0 +1,365 @@ +# -*- 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.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import page as gcdc_page +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import PagesTransport, DEFAULT_CLIENT_INFO +from .grpc import PagesGrpcTransport + + +class PagesGrpcAsyncIOTransport(PagesTransport): + """gRPC AsyncIO backend transport for Pages. + + Service for managing [Pages][google.cloud.dialogflow.cx.v3.Page]. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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_pages( + self, + ) -> Callable[[page.ListPagesRequest], Awaitable[page.ListPagesResponse]]: + r"""Return a callable for the list pages method over gRPC. + + Returns the list of all pages in the specified flow. + + Returns: + Callable[[~.ListPagesRequest], + Awaitable[~.ListPagesResponse]]: + 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_pages" not in self._stubs: + self._stubs["list_pages"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Pages/ListPages", + request_serializer=page.ListPagesRequest.serialize, + response_deserializer=page.ListPagesResponse.deserialize, + ) + return self._stubs["list_pages"] + + @property + def get_page(self) -> Callable[[page.GetPageRequest], Awaitable[page.Page]]: + r"""Return a callable for the get page method over gRPC. + + Retrieves the specified page. + + Returns: + Callable[[~.GetPageRequest], + Awaitable[~.Page]]: + 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_page" not in self._stubs: + self._stubs["get_page"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Pages/GetPage", + request_serializer=page.GetPageRequest.serialize, + response_deserializer=page.Page.deserialize, + ) + return self._stubs["get_page"] + + @property + def create_page( + self, + ) -> Callable[[gcdc_page.CreatePageRequest], Awaitable[gcdc_page.Page]]: + r"""Return a callable for the create page method over gRPC. + + Creates a page in the specified flow. + + Returns: + Callable[[~.CreatePageRequest], + Awaitable[~.Page]]: + 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_page" not in self._stubs: + self._stubs["create_page"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Pages/CreatePage", + request_serializer=gcdc_page.CreatePageRequest.serialize, + response_deserializer=gcdc_page.Page.deserialize, + ) + return self._stubs["create_page"] + + @property + def update_page( + self, + ) -> Callable[[gcdc_page.UpdatePageRequest], Awaitable[gcdc_page.Page]]: + r"""Return a callable for the update page method over gRPC. + + Updates the specified page. + + Returns: + Callable[[~.UpdatePageRequest], + Awaitable[~.Page]]: + 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_page" not in self._stubs: + self._stubs["update_page"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Pages/UpdatePage", + request_serializer=gcdc_page.UpdatePageRequest.serialize, + response_deserializer=gcdc_page.Page.deserialize, + ) + return self._stubs["update_page"] + + @property + def delete_page(self) -> Callable[[page.DeletePageRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete page method over gRPC. + + Deletes the specified page. + + Returns: + Callable[[~.DeletePageRequest], + 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_page" not in self._stubs: + self._stubs["delete_page"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Pages/DeletePage", + request_serializer=page.DeletePageRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_page"] + + +__all__ = ("PagesGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/security_settings_service/__init__.py b/google/cloud/dialogflowcx_v3/services/security_settings_service/__init__.py new file mode 100644 index 00000000..66f0a799 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/security_settings_service/__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 SecuritySettingsServiceClient +from .async_client import SecuritySettingsServiceAsyncClient + +__all__ = ( + "SecuritySettingsServiceClient", + "SecuritySettingsServiceAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/security_settings_service/async_client.py b/google/cloud/dialogflowcx_v3/services/security_settings_service/async_client.py new file mode 100644 index 00000000..40af98e0 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/security_settings_service/async_client.py @@ -0,0 +1,572 @@ +# -*- 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.dialogflowcx_v3.services.security_settings_service import pagers +from google.cloud.dialogflowcx_v3.types import security_settings +from google.cloud.dialogflowcx_v3.types import ( + security_settings as gcdc_security_settings, +) +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import SecuritySettingsServiceTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import SecuritySettingsServiceGrpcAsyncIOTransport +from .client import SecuritySettingsServiceClient + + +class SecuritySettingsServiceAsyncClient: + """Service for managing security settings for Dialogflow.""" + + _client: SecuritySettingsServiceClient + + DEFAULT_ENDPOINT = SecuritySettingsServiceClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = SecuritySettingsServiceClient.DEFAULT_MTLS_ENDPOINT + + security_settings_path = staticmethod( + SecuritySettingsServiceClient.security_settings_path + ) + parse_security_settings_path = staticmethod( + SecuritySettingsServiceClient.parse_security_settings_path + ) + + common_billing_account_path = staticmethod( + SecuritySettingsServiceClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + SecuritySettingsServiceClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(SecuritySettingsServiceClient.common_folder_path) + parse_common_folder_path = staticmethod( + SecuritySettingsServiceClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + SecuritySettingsServiceClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + SecuritySettingsServiceClient.parse_common_organization_path + ) + + common_project_path = staticmethod( + SecuritySettingsServiceClient.common_project_path + ) + parse_common_project_path = staticmethod( + SecuritySettingsServiceClient.parse_common_project_path + ) + + common_location_path = staticmethod( + SecuritySettingsServiceClient.common_location_path + ) + parse_common_location_path = staticmethod( + SecuritySettingsServiceClient.parse_common_location_path + ) + + from_service_account_file = SecuritySettingsServiceClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> SecuritySettingsServiceTransport: + """Return the transport used by the client instance. + + Returns: + SecuritySettingsServiceTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(SecuritySettingsServiceClient).get_transport_class, + type(SecuritySettingsServiceClient), + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, SecuritySettingsServiceTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the security settings service 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, ~.SecuritySettingsServiceTransport]): 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 = SecuritySettingsServiceClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def create_security_settings( + self, + request: gcdc_security_settings.CreateSecuritySettingsRequest = None, + *, + parent: str = None, + security_settings: gcdc_security_settings.SecuritySettings = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_security_settings.SecuritySettings: + r"""Create security settings in the specified location. + + Args: + request (:class:`~.gcdc_security_settings.CreateSecuritySettingsRequest`): + The request object. The request message for + [SecuritySettings.CreateSecuritySettings][]. + parent (:class:`str`): + Required. The location to create an + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings] + for. Format: + ``projects//locations/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + security_settings (:class:`~.gcdc_security_settings.SecuritySettings`): + Required. The security settings to + create. + This corresponds to the ``security_settings`` 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: + ~.gcdc_security_settings.SecuritySettings: + Represents the settings related to + security issues, such as data redaction + and data retention. It may take hours + for updates on the settings to propagate + to all the related components and take + effect. + + """ + # 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, security_settings]) + 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 = gcdc_security_settings.CreateSecuritySettingsRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if security_settings is not None: + request.security_settings = security_settings + + # 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_security_settings, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def get_security_settings( + self, + request: security_settings.GetSecuritySettingsRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> security_settings.SecuritySettings: + r"""Retrieves the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + The returned settings may be stale by up to 1 minute. + + Args: + request (:class:`~.security_settings.GetSecuritySettingsRequest`): + The request object. The request message for + [SecuritySettingsService.GetSecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettingsService.GetSecuritySettings]. + name (:class:`str`): + Required. Resource name of the settings. Format: + ``projects//locations//securitySettings/``. + 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: + ~.security_settings.SecuritySettings: + Represents the settings related to + security issues, such as data redaction + and data retention. It may take hours + for updates on the settings to propagate + to all the related components and take + effect. + + """ + # 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 = security_settings.GetSecuritySettingsRequest(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_security_settings, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def update_security_settings( + self, + request: gcdc_security_settings.UpdateSecuritySettingsRequest = None, + *, + security_settings: gcdc_security_settings.SecuritySettings = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_security_settings.SecuritySettings: + r"""Updates the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + + Args: + request (:class:`~.gcdc_security_settings.UpdateSecuritySettingsRequest`): + The request object. The request message for + [SecuritySettingsService.UpdateSecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettingsService.UpdateSecuritySettings]. + security_settings (:class:`~.gcdc_security_settings.SecuritySettings`): + Required. [SecuritySettings] object that contains values + for each of the fields to update. + This corresponds to the ``security_settings`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + Required. The mask to control which + fields get updated. If the mask is not + present, all fields will be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_security_settings.SecuritySettings: + Represents the settings related to + security issues, such as data redaction + and data retention. It may take hours + for updates on the settings to propagate + to all the related components and take + effect. + + """ + # 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([security_settings, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = gcdc_security_settings.UpdateSecuritySettingsRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if security_settings is not None: + request.security_settings = security_settings + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_security_settings, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("security_settings.name", request.security_settings.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def list_security_settings( + self, + request: security_settings.ListSecuritySettingsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListSecuritySettingsAsyncPager: + r"""Returns the list of all security settings in the + specified location. + + Args: + request (:class:`~.security_settings.ListSecuritySettingsRequest`): + The request object. The request message for + [SecuritySettings.ListSecuritySettings][]. + parent (:class:`str`): + Required. The location to list all security settings + for. Format: + ``projects//locations/``. + 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: + ~.pagers.ListSecuritySettingsAsyncPager: + The response message for + [SecuritySettings.ListSecuritySettings][]. + + 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 = security_settings.ListSecuritySettingsRequest(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_security_settings, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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.ListSecuritySettingsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def delete_security_settings( + self, + request: security_settings.DeleteSecuritySettingsRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + + Args: + request (:class:`~.security_settings.DeleteSecuritySettingsRequest`): + The request object. The request message for + [SecuritySettings.DeleteSecuritySettings][]. + name (:class:`str`): + Required. The name of the + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings] + to delete. Format: + ``projects//locations//securitySettings/``. + 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 = security_settings.DeleteSecuritySettingsRequest(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_security_settings, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("SecuritySettingsServiceAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/security_settings_service/client.py b/google/cloud/dialogflowcx_v3/services/security_settings_service/client.py new file mode 100644 index 00000000..1111403a --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/security_settings_service/client.py @@ -0,0 +1,767 @@ +# -*- 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.dialogflowcx_v3.services.security_settings_service import pagers +from google.cloud.dialogflowcx_v3.types import security_settings +from google.cloud.dialogflowcx_v3.types import ( + security_settings as gcdc_security_settings, +) +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import SecuritySettingsServiceTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import SecuritySettingsServiceGrpcTransport +from .transports.grpc_asyncio import SecuritySettingsServiceGrpcAsyncIOTransport + + +class SecuritySettingsServiceClientMeta(type): + """Metaclass for the SecuritySettingsService 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[SecuritySettingsServiceTransport]] + _transport_registry["grpc"] = SecuritySettingsServiceGrpcTransport + _transport_registry["grpc_asyncio"] = SecuritySettingsServiceGrpcAsyncIOTransport + + def get_transport_class( + cls, label: str = None, + ) -> Type[SecuritySettingsServiceTransport]: + """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 SecuritySettingsServiceClient(metaclass=SecuritySettingsServiceClientMeta): + """Service for managing security settings for Dialogflow.""" + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> SecuritySettingsServiceTransport: + """Return the transport used by the client instance. + + Returns: + SecuritySettingsServiceTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def security_settings_path( + project: str, location: str, security_settings: str, + ) -> str: + """Return a fully-qualified security_settings string.""" + return "projects/{project}/locations/{location}/securitySettings/{security_settings}".format( + project=project, location=location, security_settings=security_settings, + ) + + @staticmethod + def parse_security_settings_path(path: str) -> Dict[str, str]: + """Parse a security_settings path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/securitySettings/(?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, SecuritySettingsServiceTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the security settings service 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, ~.SecuritySettingsServiceTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, SecuritySettingsServiceTransport): + # transport is a SecuritySettingsServiceTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def create_security_settings( + self, + request: gcdc_security_settings.CreateSecuritySettingsRequest = None, + *, + parent: str = None, + security_settings: gcdc_security_settings.SecuritySettings = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_security_settings.SecuritySettings: + r"""Create security settings in the specified location. + + Args: + request (:class:`~.gcdc_security_settings.CreateSecuritySettingsRequest`): + The request object. The request message for + [SecuritySettings.CreateSecuritySettings][]. + parent (:class:`str`): + Required. The location to create an + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings] + for. Format: + ``projects//locations/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + security_settings (:class:`~.gcdc_security_settings.SecuritySettings`): + Required. The security settings to + create. + This corresponds to the ``security_settings`` 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: + ~.gcdc_security_settings.SecuritySettings: + Represents the settings related to + security issues, such as data redaction + and data retention. It may take hours + for updates on the settings to propagate + to all the related components and take + effect. + + """ + # 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, security_settings]) + 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 gcdc_security_settings.CreateSecuritySettingsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, gcdc_security_settings.CreateSecuritySettingsRequest + ): + request = gcdc_security_settings.CreateSecuritySettingsRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if security_settings is not None: + request.security_settings = security_settings + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_security_settings] + + # 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 get_security_settings( + self, + request: security_settings.GetSecuritySettingsRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> security_settings.SecuritySettings: + r"""Retrieves the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + The returned settings may be stale by up to 1 minute. + + Args: + request (:class:`~.security_settings.GetSecuritySettingsRequest`): + The request object. The request message for + [SecuritySettingsService.GetSecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettingsService.GetSecuritySettings]. + name (:class:`str`): + Required. Resource name of the settings. Format: + ``projects//locations//securitySettings/``. + 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: + ~.security_settings.SecuritySettings: + Represents the settings related to + security issues, such as data redaction + and data retention. It may take hours + for updates on the settings to propagate + to all the related components and take + effect. + + """ + # 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 security_settings.GetSecuritySettingsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, security_settings.GetSecuritySettingsRequest): + request = security_settings.GetSecuritySettingsRequest(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_security_settings] + + # 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 update_security_settings( + self, + request: gcdc_security_settings.UpdateSecuritySettingsRequest = None, + *, + security_settings: gcdc_security_settings.SecuritySettings = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_security_settings.SecuritySettings: + r"""Updates the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + + Args: + request (:class:`~.gcdc_security_settings.UpdateSecuritySettingsRequest`): + The request object. The request message for + [SecuritySettingsService.UpdateSecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettingsService.UpdateSecuritySettings]. + security_settings (:class:`~.gcdc_security_settings.SecuritySettings`): + Required. [SecuritySettings] object that contains values + for each of the fields to update. + This corresponds to the ``security_settings`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + Required. The mask to control which + fields get updated. If the mask is not + present, all fields will be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_security_settings.SecuritySettings: + Represents the settings related to + security issues, such as data redaction + and data retention. It may take hours + for updates on the settings to propagate + to all the related components and take + effect. + + """ + # 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([security_settings, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a gcdc_security_settings.UpdateSecuritySettingsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, gcdc_security_settings.UpdateSecuritySettingsRequest + ): + request = gcdc_security_settings.UpdateSecuritySettingsRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if security_settings is not None: + request.security_settings = security_settings + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_security_settings] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("security_settings.name", request.security_settings.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def list_security_settings( + self, + request: security_settings.ListSecuritySettingsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListSecuritySettingsPager: + r"""Returns the list of all security settings in the + specified location. + + Args: + request (:class:`~.security_settings.ListSecuritySettingsRequest`): + The request object. The request message for + [SecuritySettings.ListSecuritySettings][]. + parent (:class:`str`): + Required. The location to list all security settings + for. Format: + ``projects//locations/``. + 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: + ~.pagers.ListSecuritySettingsPager: + The response message for + [SecuritySettings.ListSecuritySettings][]. + + 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 security_settings.ListSecuritySettingsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, security_settings.ListSecuritySettingsRequest): + request = security_settings.ListSecuritySettingsRequest(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_security_settings] + + # 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.ListSecuritySettingsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def delete_security_settings( + self, + request: security_settings.DeleteSecuritySettingsRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + + Args: + request (:class:`~.security_settings.DeleteSecuritySettingsRequest`): + The request object. The request message for + [SecuritySettings.DeleteSecuritySettings][]. + name (:class:`str`): + Required. The name of the + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings] + to delete. Format: + ``projects//locations//securitySettings/``. + 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 security_settings.DeleteSecuritySettingsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, security_settings.DeleteSecuritySettingsRequest): + request = security_settings.DeleteSecuritySettingsRequest(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_security_settings] + + # 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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("SecuritySettingsServiceClient",) diff --git a/google/cloud/dialogflowcx_v3/services/security_settings_service/pagers.py b/google/cloud/dialogflowcx_v3/services/security_settings_service/pagers.py new file mode 100644 index 00000000..a195a16b --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/security_settings_service/pagers.py @@ -0,0 +1,152 @@ +# -*- 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 + +from google.cloud.dialogflowcx_v3.types import security_settings + + +class ListSecuritySettingsPager: + """A pager for iterating through ``list_security_settings`` requests. + + This class thinly wraps an initial + :class:`~.security_settings.ListSecuritySettingsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``security_settings`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListSecuritySettings`` requests and continue to iterate + through the ``security_settings`` field on the + corresponding responses. + + All the usual :class:`~.security_settings.ListSecuritySettingsResponse` + 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[..., security_settings.ListSecuritySettingsResponse], + request: security_settings.ListSecuritySettingsRequest, + response: security_settings.ListSecuritySettingsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.security_settings.ListSecuritySettingsRequest`): + The initial request object. + response (:class:`~.security_settings.ListSecuritySettingsResponse`): + 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 = security_settings.ListSecuritySettingsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[security_settings.ListSecuritySettingsResponse]: + 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[security_settings.SecuritySettings]: + for page in self.pages: + yield from page.security_settings + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListSecuritySettingsAsyncPager: + """A pager for iterating through ``list_security_settings`` requests. + + This class thinly wraps an initial + :class:`~.security_settings.ListSecuritySettingsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``security_settings`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListSecuritySettings`` requests and continue to iterate + through the ``security_settings`` field on the + corresponding responses. + + All the usual :class:`~.security_settings.ListSecuritySettingsResponse` + 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[security_settings.ListSecuritySettingsResponse] + ], + request: security_settings.ListSecuritySettingsRequest, + response: security_settings.ListSecuritySettingsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.security_settings.ListSecuritySettingsRequest`): + The initial request object. + response (:class:`~.security_settings.ListSecuritySettingsResponse`): + 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 = security_settings.ListSecuritySettingsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages( + self, + ) -> AsyncIterable[security_settings.ListSecuritySettingsResponse]: + 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[security_settings.SecuritySettings]: + async def async_generator(): + async for page in self.pages: + for response in page.security_settings: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/__init__.py new file mode 100644 index 00000000..6c0fb025 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/__init__.py @@ -0,0 +1,38 @@ +# -*- 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 SecuritySettingsServiceTransport +from .grpc import SecuritySettingsServiceGrpcTransport +from .grpc_asyncio import SecuritySettingsServiceGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = ( + OrderedDict() +) # type: Dict[str, Type[SecuritySettingsServiceTransport]] +_transport_registry["grpc"] = SecuritySettingsServiceGrpcTransport +_transport_registry["grpc_asyncio"] = SecuritySettingsServiceGrpcAsyncIOTransport + + +__all__ = ( + "SecuritySettingsServiceTransport", + "SecuritySettingsServiceGrpcTransport", + "SecuritySettingsServiceGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/base.py b/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/base.py new file mode 100644 index 00000000..d1bc583f --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/base.py @@ -0,0 +1,201 @@ +# -*- 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.dialogflowcx_v3.types import security_settings +from google.cloud.dialogflowcx_v3.types import ( + security_settings as gcdc_security_settings, +) +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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class SecuritySettingsServiceTransport(abc.ABC): + """Abstract transport class for SecuritySettingsService.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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.create_security_settings: gapic_v1.method.wrap_method( + self.create_security_settings, + default_timeout=None, + client_info=client_info, + ), + self.get_security_settings: gapic_v1.method.wrap_method( + self.get_security_settings, + default_timeout=None, + client_info=client_info, + ), + self.update_security_settings: gapic_v1.method.wrap_method( + self.update_security_settings, + default_timeout=None, + client_info=client_info, + ), + self.list_security_settings: gapic_v1.method.wrap_method( + self.list_security_settings, + default_timeout=None, + client_info=client_info, + ), + self.delete_security_settings: gapic_v1.method.wrap_method( + self.delete_security_settings, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def create_security_settings( + self, + ) -> typing.Callable[ + [gcdc_security_settings.CreateSecuritySettingsRequest], + typing.Union[ + gcdc_security_settings.SecuritySettings, + typing.Awaitable[gcdc_security_settings.SecuritySettings], + ], + ]: + raise NotImplementedError() + + @property + def get_security_settings( + self, + ) -> typing.Callable[ + [security_settings.GetSecuritySettingsRequest], + typing.Union[ + security_settings.SecuritySettings, + typing.Awaitable[security_settings.SecuritySettings], + ], + ]: + raise NotImplementedError() + + @property + def update_security_settings( + self, + ) -> typing.Callable[ + [gcdc_security_settings.UpdateSecuritySettingsRequest], + typing.Union[ + gcdc_security_settings.SecuritySettings, + typing.Awaitable[gcdc_security_settings.SecuritySettings], + ], + ]: + raise NotImplementedError() + + @property + def list_security_settings( + self, + ) -> typing.Callable[ + [security_settings.ListSecuritySettingsRequest], + typing.Union[ + security_settings.ListSecuritySettingsResponse, + typing.Awaitable[security_settings.ListSecuritySettingsResponse], + ], + ]: + raise NotImplementedError() + + @property + def delete_security_settings( + self, + ) -> typing.Callable[ + [security_settings.DeleteSecuritySettingsRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + +__all__ = ("SecuritySettingsServiceTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/grpc.py new file mode 100644 index 00000000..2fbefcca --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/grpc.py @@ -0,0 +1,384 @@ +# -*- 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.dialogflowcx_v3.types import security_settings +from google.cloud.dialogflowcx_v3.types import ( + security_settings as gcdc_security_settings, +) +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import SecuritySettingsServiceTransport, DEFAULT_CLIENT_INFO + + +class SecuritySettingsServiceGrpcTransport(SecuritySettingsServiceTransport): + """gRPC backend transport for SecuritySettingsService. + + Service for managing security settings for Dialogflow. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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 create_security_settings( + self, + ) -> Callable[ + [gcdc_security_settings.CreateSecuritySettingsRequest], + gcdc_security_settings.SecuritySettings, + ]: + r"""Return a callable for the create security settings method over gRPC. + + Create security settings in the specified location. + + Returns: + Callable[[~.CreateSecuritySettingsRequest], + ~.SecuritySettings]: + 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_security_settings" not in self._stubs: + self._stubs["create_security_settings"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SecuritySettingsService/CreateSecuritySettings", + request_serializer=gcdc_security_settings.CreateSecuritySettingsRequest.serialize, + response_deserializer=gcdc_security_settings.SecuritySettings.deserialize, + ) + return self._stubs["create_security_settings"] + + @property + def get_security_settings( + self, + ) -> Callable[ + [security_settings.GetSecuritySettingsRequest], + security_settings.SecuritySettings, + ]: + r"""Return a callable for the get security settings method over gRPC. + + Retrieves the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + The returned settings may be stale by up to 1 minute. + + Returns: + Callable[[~.GetSecuritySettingsRequest], + ~.SecuritySettings]: + 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_security_settings" not in self._stubs: + self._stubs["get_security_settings"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SecuritySettingsService/GetSecuritySettings", + request_serializer=security_settings.GetSecuritySettingsRequest.serialize, + response_deserializer=security_settings.SecuritySettings.deserialize, + ) + return self._stubs["get_security_settings"] + + @property + def update_security_settings( + self, + ) -> Callable[ + [gcdc_security_settings.UpdateSecuritySettingsRequest], + gcdc_security_settings.SecuritySettings, + ]: + r"""Return a callable for the update security settings method over gRPC. + + Updates the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + + Returns: + Callable[[~.UpdateSecuritySettingsRequest], + ~.SecuritySettings]: + 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_security_settings" not in self._stubs: + self._stubs["update_security_settings"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SecuritySettingsService/UpdateSecuritySettings", + request_serializer=gcdc_security_settings.UpdateSecuritySettingsRequest.serialize, + response_deserializer=gcdc_security_settings.SecuritySettings.deserialize, + ) + return self._stubs["update_security_settings"] + + @property + def list_security_settings( + self, + ) -> Callable[ + [security_settings.ListSecuritySettingsRequest], + security_settings.ListSecuritySettingsResponse, + ]: + r"""Return a callable for the list security settings method over gRPC. + + Returns the list of all security settings in the + specified location. + + Returns: + Callable[[~.ListSecuritySettingsRequest], + ~.ListSecuritySettingsResponse]: + 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_security_settings" not in self._stubs: + self._stubs["list_security_settings"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SecuritySettingsService/ListSecuritySettings", + request_serializer=security_settings.ListSecuritySettingsRequest.serialize, + response_deserializer=security_settings.ListSecuritySettingsResponse.deserialize, + ) + return self._stubs["list_security_settings"] + + @property + def delete_security_settings( + self, + ) -> Callable[[security_settings.DeleteSecuritySettingsRequest], empty.Empty]: + r"""Return a callable for the delete security settings method over gRPC. + + Deletes the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + + Returns: + Callable[[~.DeleteSecuritySettingsRequest], + ~.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_security_settings" not in self._stubs: + self._stubs["delete_security_settings"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SecuritySettingsService/DeleteSecuritySettings", + request_serializer=security_settings.DeleteSecuritySettingsRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_security_settings"] + + +__all__ = ("SecuritySettingsServiceGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/grpc_asyncio.py new file mode 100644 index 00000000..8ad82b74 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/security_settings_service/transports/grpc_asyncio.py @@ -0,0 +1,390 @@ +# -*- 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.dialogflowcx_v3.types import security_settings +from google.cloud.dialogflowcx_v3.types import ( + security_settings as gcdc_security_settings, +) +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import SecuritySettingsServiceTransport, DEFAULT_CLIENT_INFO +from .grpc import SecuritySettingsServiceGrpcTransport + + +class SecuritySettingsServiceGrpcAsyncIOTransport(SecuritySettingsServiceTransport): + """gRPC AsyncIO backend transport for SecuritySettingsService. + + Service for managing security settings for Dialogflow. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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 create_security_settings( + self, + ) -> Callable[ + [gcdc_security_settings.CreateSecuritySettingsRequest], + Awaitable[gcdc_security_settings.SecuritySettings], + ]: + r"""Return a callable for the create security settings method over gRPC. + + Create security settings in the specified location. + + Returns: + Callable[[~.CreateSecuritySettingsRequest], + Awaitable[~.SecuritySettings]]: + 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_security_settings" not in self._stubs: + self._stubs["create_security_settings"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SecuritySettingsService/CreateSecuritySettings", + request_serializer=gcdc_security_settings.CreateSecuritySettingsRequest.serialize, + response_deserializer=gcdc_security_settings.SecuritySettings.deserialize, + ) + return self._stubs["create_security_settings"] + + @property + def get_security_settings( + self, + ) -> Callable[ + [security_settings.GetSecuritySettingsRequest], + Awaitable[security_settings.SecuritySettings], + ]: + r"""Return a callable for the get security settings method over gRPC. + + Retrieves the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + The returned settings may be stale by up to 1 minute. + + Returns: + Callable[[~.GetSecuritySettingsRequest], + Awaitable[~.SecuritySettings]]: + 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_security_settings" not in self._stubs: + self._stubs["get_security_settings"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SecuritySettingsService/GetSecuritySettings", + request_serializer=security_settings.GetSecuritySettingsRequest.serialize, + response_deserializer=security_settings.SecuritySettings.deserialize, + ) + return self._stubs["get_security_settings"] + + @property + def update_security_settings( + self, + ) -> Callable[ + [gcdc_security_settings.UpdateSecuritySettingsRequest], + Awaitable[gcdc_security_settings.SecuritySettings], + ]: + r"""Return a callable for the update security settings method over gRPC. + + Updates the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + + Returns: + Callable[[~.UpdateSecuritySettingsRequest], + Awaitable[~.SecuritySettings]]: + 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_security_settings" not in self._stubs: + self._stubs["update_security_settings"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SecuritySettingsService/UpdateSecuritySettings", + request_serializer=gcdc_security_settings.UpdateSecuritySettingsRequest.serialize, + response_deserializer=gcdc_security_settings.SecuritySettings.deserialize, + ) + return self._stubs["update_security_settings"] + + @property + def list_security_settings( + self, + ) -> Callable[ + [security_settings.ListSecuritySettingsRequest], + Awaitable[security_settings.ListSecuritySettingsResponse], + ]: + r"""Return a callable for the list security settings method over gRPC. + + Returns the list of all security settings in the + specified location. + + Returns: + Callable[[~.ListSecuritySettingsRequest], + Awaitable[~.ListSecuritySettingsResponse]]: + 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_security_settings" not in self._stubs: + self._stubs["list_security_settings"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SecuritySettingsService/ListSecuritySettings", + request_serializer=security_settings.ListSecuritySettingsRequest.serialize, + response_deserializer=security_settings.ListSecuritySettingsResponse.deserialize, + ) + return self._stubs["list_security_settings"] + + @property + def delete_security_settings( + self, + ) -> Callable[ + [security_settings.DeleteSecuritySettingsRequest], Awaitable[empty.Empty] + ]: + r"""Return a callable for the delete security settings method over gRPC. + + Deletes the specified + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings]. + + Returns: + Callable[[~.DeleteSecuritySettingsRequest], + 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_security_settings" not in self._stubs: + self._stubs["delete_security_settings"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SecuritySettingsService/DeleteSecuritySettings", + request_serializer=security_settings.DeleteSecuritySettingsRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_security_settings"] + + +__all__ = ("SecuritySettingsServiceGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/session_entity_types/__init__.py b/google/cloud/dialogflowcx_v3/services/session_entity_types/__init__.py new file mode 100644 index 00000000..c0dca267 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/session_entity_types/__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 SessionEntityTypesClient +from .async_client import SessionEntityTypesAsyncClient + +__all__ = ( + "SessionEntityTypesClient", + "SessionEntityTypesAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/session_entity_types/async_client.py b/google/cloud/dialogflowcx_v3/services/session_entity_types/async_client.py new file mode 100644 index 00000000..1e3c1f38 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/session_entity_types/async_client.py @@ -0,0 +1,619 @@ +# -*- 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.dialogflowcx_v3.services.session_entity_types import pagers +from google.cloud.dialogflowcx_v3.types import entity_type +from google.cloud.dialogflowcx_v3.types import session_entity_type +from google.cloud.dialogflowcx_v3.types import ( + session_entity_type as gcdc_session_entity_type, +) +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import SessionEntityTypesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import SessionEntityTypesGrpcAsyncIOTransport +from .client import SessionEntityTypesClient + + +class SessionEntityTypesAsyncClient: + """Service for managing + [SessionEntityTypes][google.cloud.dialogflow.cx.v3.SessionEntityType]. + """ + + _client: SessionEntityTypesClient + + DEFAULT_ENDPOINT = SessionEntityTypesClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = SessionEntityTypesClient.DEFAULT_MTLS_ENDPOINT + + session_entity_type_path = staticmethod( + SessionEntityTypesClient.session_entity_type_path + ) + parse_session_entity_type_path = staticmethod( + SessionEntityTypesClient.parse_session_entity_type_path + ) + + common_billing_account_path = staticmethod( + SessionEntityTypesClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + SessionEntityTypesClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(SessionEntityTypesClient.common_folder_path) + parse_common_folder_path = staticmethod( + SessionEntityTypesClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + SessionEntityTypesClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + SessionEntityTypesClient.parse_common_organization_path + ) + + common_project_path = staticmethod(SessionEntityTypesClient.common_project_path) + parse_common_project_path = staticmethod( + SessionEntityTypesClient.parse_common_project_path + ) + + common_location_path = staticmethod(SessionEntityTypesClient.common_location_path) + parse_common_location_path = staticmethod( + SessionEntityTypesClient.parse_common_location_path + ) + + from_service_account_file = SessionEntityTypesClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> SessionEntityTypesTransport: + """Return the transport used by the client instance. + + Returns: + SessionEntityTypesTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(SessionEntityTypesClient).get_transport_class, + type(SessionEntityTypesClient), + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, SessionEntityTypesTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the session entity types 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, ~.SessionEntityTypesTransport]): 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 = SessionEntityTypesClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_session_entity_types( + self, + request: session_entity_type.ListSessionEntityTypesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListSessionEntityTypesAsyncPager: + r"""Returns the list of all session entity types in the + specified session. + + Args: + request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + The request object. The request message for + [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.cx.v3.SessionEntityTypes.ListSessionEntityTypes]. + parent (:class:`str`): + Required. The session to list all session entity types + from. Format: + ``projects//locations//agents//sessions/`` + or + ``projects//locations//agents//environments//sessions/``. + If ``Environment ID`` is not specified, we assume + default 'draft' environment. + 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: + ~.pagers.ListSessionEntityTypesAsyncPager: + The response message for + [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.cx.v3.SessionEntityTypes.ListSessionEntityTypes]. + + 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 = session_entity_type.ListSessionEntityTypesRequest(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_session_entity_types, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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.ListSessionEntityTypesAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_session_entity_type( + self, + request: session_entity_type.GetSessionEntityTypeRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> session_entity_type.SessionEntityType: + r"""Retrieves the specified session entity type. + + Args: + request (:class:`~.session_entity_type.GetSessionEntityTypeRequest`): + The request object. The request message for + [SessionEntityTypes.GetSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.GetSessionEntityType]. + name (:class:`str`): + Required. The name of the session entity type. Format: + ``projects//locations//agents//sessions//entityTypes/`` + or + ``projects//locations//agents//environments//sessions//entityTypes/``. + If ``Environment ID`` is not specified, we assume + default 'draft' environment. + 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: + ~.session_entity_type.SessionEntityType: + Session entity types are referred to as **User** entity + types and are entities that are built for an individual + user such as favorites, preferences, playlists, and so + on. + + You can redefine a session entity type at the session + level to extend or replace a [custom entity + type][google.cloud.dialogflow.cx.v3.EntityType] at the + user session level (we refer to the entity types defined + at the agent level as "custom entity types"). + + Note: session entity types apply to all queries, + regardless of the language. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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 = session_entity_type.GetSessionEntityTypeRequest(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_session_entity_type, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_session_entity_type( + self, + request: gcdc_session_entity_type.CreateSessionEntityTypeRequest = None, + *, + parent: str = None, + session_entity_type: gcdc_session_entity_type.SessionEntityType = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_session_entity_type.SessionEntityType: + r"""Creates a session entity type. + If the specified session entity type already exists, + overrides the session entity type. + + Args: + request (:class:`~.gcdc_session_entity_type.CreateSessionEntityTypeRequest`): + The request object. The request message for + [SessionEntityTypes.CreateSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.CreateSessionEntityType]. + parent (:class:`str`): + Required. The session to create a session entity type + for. Format: + ``projects//locations//agents//sessions/`` + or + ``projects//locations//agents//environments//sessions/``. + If ``Environment ID`` is not specified, we assume + default 'draft' environment. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + session_entity_type (:class:`~.gcdc_session_entity_type.SessionEntityType`): + Required. The session entity type to + create. + This corresponds to the ``session_entity_type`` 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: + ~.gcdc_session_entity_type.SessionEntityType: + Session entity types are referred to as **User** entity + types and are entities that are built for an individual + user such as favorites, preferences, playlists, and so + on. + + You can redefine a session entity type at the session + level to extend or replace a [custom entity + type][google.cloud.dialogflow.cx.v3.EntityType] at the + user session level (we refer to the entity types defined + at the agent level as "custom entity types"). + + Note: session entity types apply to all queries, + regardless of the language. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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, session_entity_type]) + 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 = gcdc_session_entity_type.CreateSessionEntityTypeRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if session_entity_type is not None: + request.session_entity_type = session_entity_type + + # 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_session_entity_type, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_session_entity_type( + self, + request: gcdc_session_entity_type.UpdateSessionEntityTypeRequest = None, + *, + session_entity_type: gcdc_session_entity_type.SessionEntityType = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_session_entity_type.SessionEntityType: + r"""Updates the specified session entity type. + + Args: + request (:class:`~.gcdc_session_entity_type.UpdateSessionEntityTypeRequest`): + The request object. The request message for + [SessionEntityTypes.UpdateSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.UpdateSessionEntityType]. + session_entity_type (:class:`~.gcdc_session_entity_type.SessionEntityType`): + Required. The session entity type to update. Format: + ``projects//locations//agents//sessions//entityTypes/`` + or + ``projects//locations//agents//environments//sessions//entityTypes/``. + If ``Environment ID`` is not specified, we assume + default 'draft' environment. + This corresponds to the ``session_entity_type`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_session_entity_type.SessionEntityType: + Session entity types are referred to as **User** entity + types and are entities that are built for an individual + user such as favorites, preferences, playlists, and so + on. + + You can redefine a session entity type at the session + level to extend or replace a [custom entity + type][google.cloud.dialogflow.cx.v3.EntityType] at the + user session level (we refer to the entity types defined + at the agent level as "custom entity types"). + + Note: session entity types apply to all queries, + regardless of the language. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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([session_entity_type, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = gcdc_session_entity_type.UpdateSessionEntityTypeRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if session_entity_type is not None: + request.session_entity_type = session_entity_type + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_session_entity_type, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("session_entity_type.name", request.session_entity_type.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_session_entity_type( + self, + request: session_entity_type.DeleteSessionEntityTypeRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified session entity type. + + Args: + request (:class:`~.session_entity_type.DeleteSessionEntityTypeRequest`): + The request object. The request message for + [SessionEntityTypes.DeleteSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.DeleteSessionEntityType]. + name (:class:`str`): + Required. The name of the session entity type to delete. + Format: + ``projects//locations//agents//sessions//entityTypes/`` + or + ``projects//locations//agents//environments//sessions//entityTypes/``. + If ``Environment ID`` is not specified, we assume + default 'draft' environment. + 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 = session_entity_type.DeleteSessionEntityTypeRequest(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_session_entity_type, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("SessionEntityTypesAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/session_entity_types/client.py b/google/cloud/dialogflowcx_v3/services/session_entity_types/client.py new file mode 100644 index 00000000..ec12fca3 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/session_entity_types/client.py @@ -0,0 +1,830 @@ +# -*- 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.dialogflowcx_v3.services.session_entity_types import pagers +from google.cloud.dialogflowcx_v3.types import entity_type +from google.cloud.dialogflowcx_v3.types import session_entity_type +from google.cloud.dialogflowcx_v3.types import ( + session_entity_type as gcdc_session_entity_type, +) +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import SessionEntityTypesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import SessionEntityTypesGrpcTransport +from .transports.grpc_asyncio import SessionEntityTypesGrpcAsyncIOTransport + + +class SessionEntityTypesClientMeta(type): + """Metaclass for the SessionEntityTypes 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[SessionEntityTypesTransport]] + _transport_registry["grpc"] = SessionEntityTypesGrpcTransport + _transport_registry["grpc_asyncio"] = SessionEntityTypesGrpcAsyncIOTransport + + def get_transport_class( + cls, label: str = None, + ) -> Type[SessionEntityTypesTransport]: + """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 SessionEntityTypesClient(metaclass=SessionEntityTypesClientMeta): + """Service for managing + [SessionEntityTypes][google.cloud.dialogflow.cx.v3.SessionEntityType]. + """ + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> SessionEntityTypesTransport: + """Return the transport used by the client instance. + + Returns: + SessionEntityTypesTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def session_entity_type_path( + project: str, location: str, agent: str, session: str, entity_type: str, + ) -> str: + """Return a fully-qualified session_entity_type string.""" + return "projects/{project}/locations/{location}/agents/{agent}/sessions/{session}/entityTypes/{entity_type}".format( + project=project, + location=location, + agent=agent, + session=session, + entity_type=entity_type, + ) + + @staticmethod + def parse_session_entity_type_path(path: str) -> Dict[str, str]: + """Parse a session_entity_type path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/sessions/(?P.+?)/entityTypes/(?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, SessionEntityTypesTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the session entity types 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, ~.SessionEntityTypesTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, SessionEntityTypesTransport): + # transport is a SessionEntityTypesTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_session_entity_types( + self, + request: session_entity_type.ListSessionEntityTypesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListSessionEntityTypesPager: + r"""Returns the list of all session entity types in the + specified session. + + Args: + request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + The request object. The request message for + [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.cx.v3.SessionEntityTypes.ListSessionEntityTypes]. + parent (:class:`str`): + Required. The session to list all session entity types + from. Format: + ``projects//locations//agents//sessions/`` + or + ``projects//locations//agents//environments//sessions/``. + If ``Environment ID`` is not specified, we assume + default 'draft' environment. + 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: + ~.pagers.ListSessionEntityTypesPager: + The response message for + [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.cx.v3.SessionEntityTypes.ListSessionEntityTypes]. + + 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 session_entity_type.ListSessionEntityTypesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, session_entity_type.ListSessionEntityTypesRequest): + request = session_entity_type.ListSessionEntityTypesRequest(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_session_entity_types + ] + + # 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.ListSessionEntityTypesPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_session_entity_type( + self, + request: session_entity_type.GetSessionEntityTypeRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> session_entity_type.SessionEntityType: + r"""Retrieves the specified session entity type. + + Args: + request (:class:`~.session_entity_type.GetSessionEntityTypeRequest`): + The request object. The request message for + [SessionEntityTypes.GetSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.GetSessionEntityType]. + name (:class:`str`): + Required. The name of the session entity type. Format: + ``projects//locations//agents//sessions//entityTypes/`` + or + ``projects//locations//agents//environments//sessions//entityTypes/``. + If ``Environment ID`` is not specified, we assume + default 'draft' environment. + 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: + ~.session_entity_type.SessionEntityType: + Session entity types are referred to as **User** entity + types and are entities that are built for an individual + user such as favorites, preferences, playlists, and so + on. + + You can redefine a session entity type at the session + level to extend or replace a [custom entity + type][google.cloud.dialogflow.cx.v3.EntityType] at the + user session level (we refer to the entity types defined + at the agent level as "custom entity types"). + + Note: session entity types apply to all queries, + regardless of the language. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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 session_entity_type.GetSessionEntityTypeRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, session_entity_type.GetSessionEntityTypeRequest): + request = session_entity_type.GetSessionEntityTypeRequest(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_session_entity_type] + + # 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_session_entity_type( + self, + request: gcdc_session_entity_type.CreateSessionEntityTypeRequest = None, + *, + parent: str = None, + session_entity_type: gcdc_session_entity_type.SessionEntityType = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_session_entity_type.SessionEntityType: + r"""Creates a session entity type. + If the specified session entity type already exists, + overrides the session entity type. + + Args: + request (:class:`~.gcdc_session_entity_type.CreateSessionEntityTypeRequest`): + The request object. The request message for + [SessionEntityTypes.CreateSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.CreateSessionEntityType]. + parent (:class:`str`): + Required. The session to create a session entity type + for. Format: + ``projects//locations//agents//sessions/`` + or + ``projects//locations//agents//environments//sessions/``. + If ``Environment ID`` is not specified, we assume + default 'draft' environment. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + session_entity_type (:class:`~.gcdc_session_entity_type.SessionEntityType`): + Required. The session entity type to + create. + This corresponds to the ``session_entity_type`` 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: + ~.gcdc_session_entity_type.SessionEntityType: + Session entity types are referred to as **User** entity + types and are entities that are built for an individual + user such as favorites, preferences, playlists, and so + on. + + You can redefine a session entity type at the session + level to extend or replace a [custom entity + type][google.cloud.dialogflow.cx.v3.EntityType] at the + user session level (we refer to the entity types defined + at the agent level as "custom entity types"). + + Note: session entity types apply to all queries, + regardless of the language. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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, session_entity_type]) + 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 gcdc_session_entity_type.CreateSessionEntityTypeRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, gcdc_session_entity_type.CreateSessionEntityTypeRequest + ): + request = gcdc_session_entity_type.CreateSessionEntityTypeRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if session_entity_type is not None: + request.session_entity_type = session_entity_type + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[ + self._transport.create_session_entity_type + ] + + # 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_session_entity_type( + self, + request: gcdc_session_entity_type.UpdateSessionEntityTypeRequest = None, + *, + session_entity_type: gcdc_session_entity_type.SessionEntityType = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_session_entity_type.SessionEntityType: + r"""Updates the specified session entity type. + + Args: + request (:class:`~.gcdc_session_entity_type.UpdateSessionEntityTypeRequest`): + The request object. The request message for + [SessionEntityTypes.UpdateSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.UpdateSessionEntityType]. + session_entity_type (:class:`~.gcdc_session_entity_type.SessionEntityType`): + Required. The session entity type to update. Format: + ``projects//locations//agents//sessions//entityTypes/`` + or + ``projects//locations//agents//environments//sessions//entityTypes/``. + If ``Environment ID`` is not specified, we assume + default 'draft' environment. + This corresponds to the ``session_entity_type`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_session_entity_type.SessionEntityType: + Session entity types are referred to as **User** entity + types and are entities that are built for an individual + user such as favorites, preferences, playlists, and so + on. + + You can redefine a session entity type at the session + level to extend or replace a [custom entity + type][google.cloud.dialogflow.cx.v3.EntityType] at the + user session level (we refer to the entity types defined + at the agent level as "custom entity types"). + + Note: session entity types apply to all queries, + regardless of the language. + + For more information about entity types, see the + `Dialogflow + documentation `__. + + """ + # 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([session_entity_type, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a gcdc_session_entity_type.UpdateSessionEntityTypeRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, gcdc_session_entity_type.UpdateSessionEntityTypeRequest + ): + request = gcdc_session_entity_type.UpdateSessionEntityTypeRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if session_entity_type is not None: + request.session_entity_type = session_entity_type + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[ + self._transport.update_session_entity_type + ] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("session_entity_type.name", request.session_entity_type.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def delete_session_entity_type( + self, + request: session_entity_type.DeleteSessionEntityTypeRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified session entity type. + + Args: + request (:class:`~.session_entity_type.DeleteSessionEntityTypeRequest`): + The request object. The request message for + [SessionEntityTypes.DeleteSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.DeleteSessionEntityType]. + name (:class:`str`): + Required. The name of the session entity type to delete. + Format: + ``projects//locations//agents//sessions//entityTypes/`` + or + ``projects//locations//agents//environments//sessions//entityTypes/``. + If ``Environment ID`` is not specified, we assume + default 'draft' environment. + 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 session_entity_type.DeleteSessionEntityTypeRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, session_entity_type.DeleteSessionEntityTypeRequest): + request = session_entity_type.DeleteSessionEntityTypeRequest(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_session_entity_type + ] + + # 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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("SessionEntityTypesClient",) diff --git a/google/cloud/dialogflowcx_v3/services/session_entity_types/pagers.py b/google/cloud/dialogflowcx_v3/services/session_entity_types/pagers.py new file mode 100644 index 00000000..89264424 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/session_entity_types/pagers.py @@ -0,0 +1,152 @@ +# -*- 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 + +from google.cloud.dialogflowcx_v3.types import session_entity_type + + +class ListSessionEntityTypesPager: + """A pager for iterating through ``list_session_entity_types`` requests. + + This class thinly wraps an initial + :class:`~.session_entity_type.ListSessionEntityTypesResponse` object, and + provides an ``__iter__`` method to iterate through its + ``session_entity_types`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListSessionEntityTypes`` requests and continue to iterate + through the ``session_entity_types`` field on the + corresponding responses. + + All the usual :class:`~.session_entity_type.ListSessionEntityTypesResponse` + 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[..., session_entity_type.ListSessionEntityTypesResponse], + request: session_entity_type.ListSessionEntityTypesRequest, + response: session_entity_type.ListSessionEntityTypesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + The initial request object. + response (:class:`~.session_entity_type.ListSessionEntityTypesResponse`): + 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 = session_entity_type.ListSessionEntityTypesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[session_entity_type.ListSessionEntityTypesResponse]: + 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[session_entity_type.SessionEntityType]: + for page in self.pages: + yield from page.session_entity_types + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListSessionEntityTypesAsyncPager: + """A pager for iterating through ``list_session_entity_types`` requests. + + This class thinly wraps an initial + :class:`~.session_entity_type.ListSessionEntityTypesResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``session_entity_types`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListSessionEntityTypes`` requests and continue to iterate + through the ``session_entity_types`` field on the + corresponding responses. + + All the usual :class:`~.session_entity_type.ListSessionEntityTypesResponse` + 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[session_entity_type.ListSessionEntityTypesResponse] + ], + request: session_entity_type.ListSessionEntityTypesRequest, + response: session_entity_type.ListSessionEntityTypesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + The initial request object. + response (:class:`~.session_entity_type.ListSessionEntityTypesResponse`): + 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 = session_entity_type.ListSessionEntityTypesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages( + self, + ) -> AsyncIterable[session_entity_type.ListSessionEntityTypesResponse]: + 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[session_entity_type.SessionEntityType]: + async def async_generator(): + async for page in self.pages: + for response in page.session_entity_types: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/__init__.py new file mode 100644 index 00000000..7ab5a895 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/__init__.py @@ -0,0 +1,38 @@ +# -*- 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 SessionEntityTypesTransport +from .grpc import SessionEntityTypesGrpcTransport +from .grpc_asyncio import SessionEntityTypesGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = ( + OrderedDict() +) # type: Dict[str, Type[SessionEntityTypesTransport]] +_transport_registry["grpc"] = SessionEntityTypesGrpcTransport +_transport_registry["grpc_asyncio"] = SessionEntityTypesGrpcAsyncIOTransport + + +__all__ = ( + "SessionEntityTypesTransport", + "SessionEntityTypesGrpcTransport", + "SessionEntityTypesGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/base.py b/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/base.py new file mode 100644 index 00000000..54ae7a03 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/base.py @@ -0,0 +1,201 @@ +# -*- 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.dialogflowcx_v3.types import session_entity_type +from google.cloud.dialogflowcx_v3.types import ( + session_entity_type as gcdc_session_entity_type, +) +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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class SessionEntityTypesTransport(abc.ABC): + """Abstract transport class for SessionEntityTypes.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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_session_entity_types: gapic_v1.method.wrap_method( + self.list_session_entity_types, + default_timeout=None, + client_info=client_info, + ), + self.get_session_entity_type: gapic_v1.method.wrap_method( + self.get_session_entity_type, + default_timeout=None, + client_info=client_info, + ), + self.create_session_entity_type: gapic_v1.method.wrap_method( + self.create_session_entity_type, + default_timeout=None, + client_info=client_info, + ), + self.update_session_entity_type: gapic_v1.method.wrap_method( + self.update_session_entity_type, + default_timeout=None, + client_info=client_info, + ), + self.delete_session_entity_type: gapic_v1.method.wrap_method( + self.delete_session_entity_type, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def list_session_entity_types( + self, + ) -> typing.Callable[ + [session_entity_type.ListSessionEntityTypesRequest], + typing.Union[ + session_entity_type.ListSessionEntityTypesResponse, + typing.Awaitable[session_entity_type.ListSessionEntityTypesResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_session_entity_type( + self, + ) -> typing.Callable[ + [session_entity_type.GetSessionEntityTypeRequest], + typing.Union[ + session_entity_type.SessionEntityType, + typing.Awaitable[session_entity_type.SessionEntityType], + ], + ]: + raise NotImplementedError() + + @property + def create_session_entity_type( + self, + ) -> typing.Callable[ + [gcdc_session_entity_type.CreateSessionEntityTypeRequest], + typing.Union[ + gcdc_session_entity_type.SessionEntityType, + typing.Awaitable[gcdc_session_entity_type.SessionEntityType], + ], + ]: + raise NotImplementedError() + + @property + def update_session_entity_type( + self, + ) -> typing.Callable[ + [gcdc_session_entity_type.UpdateSessionEntityTypeRequest], + typing.Union[ + gcdc_session_entity_type.SessionEntityType, + typing.Awaitable[gcdc_session_entity_type.SessionEntityType], + ], + ]: + raise NotImplementedError() + + @property + def delete_session_entity_type( + self, + ) -> typing.Callable[ + [session_entity_type.DeleteSessionEntityTypeRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + +__all__ = ("SessionEntityTypesTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/grpc.py new file mode 100644 index 00000000..8eb837f9 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/grpc.py @@ -0,0 +1,383 @@ +# -*- 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.dialogflowcx_v3.types import session_entity_type +from google.cloud.dialogflowcx_v3.types import ( + session_entity_type as gcdc_session_entity_type, +) +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import SessionEntityTypesTransport, DEFAULT_CLIENT_INFO + + +class SessionEntityTypesGrpcTransport(SessionEntityTypesTransport): + """gRPC backend transport for SessionEntityTypes. + + Service for managing + [SessionEntityTypes][google.cloud.dialogflow.cx.v3.SessionEntityType]. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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_session_entity_types( + self, + ) -> Callable[ + [session_entity_type.ListSessionEntityTypesRequest], + session_entity_type.ListSessionEntityTypesResponse, + ]: + r"""Return a callable for the list session entity types method over gRPC. + + Returns the list of all session entity types in the + specified session. + + Returns: + Callable[[~.ListSessionEntityTypesRequest], + ~.ListSessionEntityTypesResponse]: + 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_session_entity_types" not in self._stubs: + self._stubs["list_session_entity_types"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SessionEntityTypes/ListSessionEntityTypes", + request_serializer=session_entity_type.ListSessionEntityTypesRequest.serialize, + response_deserializer=session_entity_type.ListSessionEntityTypesResponse.deserialize, + ) + return self._stubs["list_session_entity_types"] + + @property + def get_session_entity_type( + self, + ) -> Callable[ + [session_entity_type.GetSessionEntityTypeRequest], + session_entity_type.SessionEntityType, + ]: + r"""Return a callable for the get session entity type method over gRPC. + + Retrieves the specified session entity type. + + Returns: + Callable[[~.GetSessionEntityTypeRequest], + ~.SessionEntityType]: + 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_session_entity_type" not in self._stubs: + self._stubs["get_session_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SessionEntityTypes/GetSessionEntityType", + request_serializer=session_entity_type.GetSessionEntityTypeRequest.serialize, + response_deserializer=session_entity_type.SessionEntityType.deserialize, + ) + return self._stubs["get_session_entity_type"] + + @property + def create_session_entity_type( + self, + ) -> Callable[ + [gcdc_session_entity_type.CreateSessionEntityTypeRequest], + gcdc_session_entity_type.SessionEntityType, + ]: + r"""Return a callable for the create session entity type method over gRPC. + + Creates a session entity type. + If the specified session entity type already exists, + overrides the session entity type. + + Returns: + Callable[[~.CreateSessionEntityTypeRequest], + ~.SessionEntityType]: + 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_session_entity_type" not in self._stubs: + self._stubs["create_session_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SessionEntityTypes/CreateSessionEntityType", + request_serializer=gcdc_session_entity_type.CreateSessionEntityTypeRequest.serialize, + response_deserializer=gcdc_session_entity_type.SessionEntityType.deserialize, + ) + return self._stubs["create_session_entity_type"] + + @property + def update_session_entity_type( + self, + ) -> Callable[ + [gcdc_session_entity_type.UpdateSessionEntityTypeRequest], + gcdc_session_entity_type.SessionEntityType, + ]: + r"""Return a callable for the update session entity type method over gRPC. + + Updates the specified session entity type. + + Returns: + Callable[[~.UpdateSessionEntityTypeRequest], + ~.SessionEntityType]: + 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_session_entity_type" not in self._stubs: + self._stubs["update_session_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SessionEntityTypes/UpdateSessionEntityType", + request_serializer=gcdc_session_entity_type.UpdateSessionEntityTypeRequest.serialize, + response_deserializer=gcdc_session_entity_type.SessionEntityType.deserialize, + ) + return self._stubs["update_session_entity_type"] + + @property + def delete_session_entity_type( + self, + ) -> Callable[[session_entity_type.DeleteSessionEntityTypeRequest], empty.Empty]: + r"""Return a callable for the delete session entity type method over gRPC. + + Deletes the specified session entity type. + + Returns: + Callable[[~.DeleteSessionEntityTypeRequest], + ~.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_session_entity_type" not in self._stubs: + self._stubs["delete_session_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SessionEntityTypes/DeleteSessionEntityType", + request_serializer=session_entity_type.DeleteSessionEntityTypeRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_session_entity_type"] + + +__all__ = ("SessionEntityTypesGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/grpc_asyncio.py new file mode 100644 index 00000000..2b672a5f --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/session_entity_types/transports/grpc_asyncio.py @@ -0,0 +1,389 @@ +# -*- 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.dialogflowcx_v3.types import session_entity_type +from google.cloud.dialogflowcx_v3.types import ( + session_entity_type as gcdc_session_entity_type, +) +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import SessionEntityTypesTransport, DEFAULT_CLIENT_INFO +from .grpc import SessionEntityTypesGrpcTransport + + +class SessionEntityTypesGrpcAsyncIOTransport(SessionEntityTypesTransport): + """gRPC AsyncIO backend transport for SessionEntityTypes. + + Service for managing + [SessionEntityTypes][google.cloud.dialogflow.cx.v3.SessionEntityType]. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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_session_entity_types( + self, + ) -> Callable[ + [session_entity_type.ListSessionEntityTypesRequest], + Awaitable[session_entity_type.ListSessionEntityTypesResponse], + ]: + r"""Return a callable for the list session entity types method over gRPC. + + Returns the list of all session entity types in the + specified session. + + Returns: + Callable[[~.ListSessionEntityTypesRequest], + Awaitable[~.ListSessionEntityTypesResponse]]: + 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_session_entity_types" not in self._stubs: + self._stubs["list_session_entity_types"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SessionEntityTypes/ListSessionEntityTypes", + request_serializer=session_entity_type.ListSessionEntityTypesRequest.serialize, + response_deserializer=session_entity_type.ListSessionEntityTypesResponse.deserialize, + ) + return self._stubs["list_session_entity_types"] + + @property + def get_session_entity_type( + self, + ) -> Callable[ + [session_entity_type.GetSessionEntityTypeRequest], + Awaitable[session_entity_type.SessionEntityType], + ]: + r"""Return a callable for the get session entity type method over gRPC. + + Retrieves the specified session entity type. + + Returns: + Callable[[~.GetSessionEntityTypeRequest], + Awaitable[~.SessionEntityType]]: + 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_session_entity_type" not in self._stubs: + self._stubs["get_session_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SessionEntityTypes/GetSessionEntityType", + request_serializer=session_entity_type.GetSessionEntityTypeRequest.serialize, + response_deserializer=session_entity_type.SessionEntityType.deserialize, + ) + return self._stubs["get_session_entity_type"] + + @property + def create_session_entity_type( + self, + ) -> Callable[ + [gcdc_session_entity_type.CreateSessionEntityTypeRequest], + Awaitable[gcdc_session_entity_type.SessionEntityType], + ]: + r"""Return a callable for the create session entity type method over gRPC. + + Creates a session entity type. + If the specified session entity type already exists, + overrides the session entity type. + + Returns: + Callable[[~.CreateSessionEntityTypeRequest], + Awaitable[~.SessionEntityType]]: + 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_session_entity_type" not in self._stubs: + self._stubs["create_session_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SessionEntityTypes/CreateSessionEntityType", + request_serializer=gcdc_session_entity_type.CreateSessionEntityTypeRequest.serialize, + response_deserializer=gcdc_session_entity_type.SessionEntityType.deserialize, + ) + return self._stubs["create_session_entity_type"] + + @property + def update_session_entity_type( + self, + ) -> Callable[ + [gcdc_session_entity_type.UpdateSessionEntityTypeRequest], + Awaitable[gcdc_session_entity_type.SessionEntityType], + ]: + r"""Return a callable for the update session entity type method over gRPC. + + Updates the specified session entity type. + + Returns: + Callable[[~.UpdateSessionEntityTypeRequest], + Awaitable[~.SessionEntityType]]: + 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_session_entity_type" not in self._stubs: + self._stubs["update_session_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SessionEntityTypes/UpdateSessionEntityType", + request_serializer=gcdc_session_entity_type.UpdateSessionEntityTypeRequest.serialize, + response_deserializer=gcdc_session_entity_type.SessionEntityType.deserialize, + ) + return self._stubs["update_session_entity_type"] + + @property + def delete_session_entity_type( + self, + ) -> Callable[ + [session_entity_type.DeleteSessionEntityTypeRequest], Awaitable[empty.Empty] + ]: + r"""Return a callable for the delete session entity type method over gRPC. + + Deletes the specified session entity type. + + Returns: + Callable[[~.DeleteSessionEntityTypeRequest], + 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_session_entity_type" not in self._stubs: + self._stubs["delete_session_entity_type"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.SessionEntityTypes/DeleteSessionEntityType", + request_serializer=session_entity_type.DeleteSessionEntityTypeRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_session_entity_type"] + + +__all__ = ("SessionEntityTypesGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/sessions/__init__.py b/google/cloud/dialogflowcx_v3/services/sessions/__init__.py new file mode 100644 index 00000000..3b2a59a9 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/sessions/__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 SessionsClient +from .async_client import SessionsAsyncClient + +__all__ = ( + "SessionsClient", + "SessionsAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/sessions/async_client.py b/google/cloud/dialogflowcx_v3/services/sessions/async_client.py new file mode 100644 index 00000000..624df4af --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/sessions/async_client.py @@ -0,0 +1,437 @@ +# -*- 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, + AsyncIterable, + Awaitable, + AsyncIterator, + 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.dialogflowcx_v3.types import audio_config +from google.cloud.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import session + +from .transports.base import SessionsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import SessionsGrpcAsyncIOTransport +from .client import SessionsClient + + +class SessionsAsyncClient: + """A session represents an interaction with a user. You retrieve user + input and pass it to the + [DetectIntent][google.cloud.dialogflow.cx.v3.Sessions.DetectIntent] + method to determine user intent and respond. + """ + + _client: SessionsClient + + DEFAULT_ENDPOINT = SessionsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = SessionsClient.DEFAULT_MTLS_ENDPOINT + + entity_type_path = staticmethod(SessionsClient.entity_type_path) + parse_entity_type_path = staticmethod(SessionsClient.parse_entity_type_path) + flow_path = staticmethod(SessionsClient.flow_path) + parse_flow_path = staticmethod(SessionsClient.parse_flow_path) + intent_path = staticmethod(SessionsClient.intent_path) + parse_intent_path = staticmethod(SessionsClient.parse_intent_path) + page_path = staticmethod(SessionsClient.page_path) + parse_page_path = staticmethod(SessionsClient.parse_page_path) + session_path = staticmethod(SessionsClient.session_path) + parse_session_path = staticmethod(SessionsClient.parse_session_path) + session_entity_type_path = staticmethod(SessionsClient.session_entity_type_path) + parse_session_entity_type_path = staticmethod( + SessionsClient.parse_session_entity_type_path + ) + transition_route_group_path = staticmethod( + SessionsClient.transition_route_group_path + ) + parse_transition_route_group_path = staticmethod( + SessionsClient.parse_transition_route_group_path + ) + webhook_path = staticmethod(SessionsClient.webhook_path) + parse_webhook_path = staticmethod(SessionsClient.parse_webhook_path) + + common_billing_account_path = staticmethod( + SessionsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + SessionsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(SessionsClient.common_folder_path) + parse_common_folder_path = staticmethod(SessionsClient.parse_common_folder_path) + + common_organization_path = staticmethod(SessionsClient.common_organization_path) + parse_common_organization_path = staticmethod( + SessionsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(SessionsClient.common_project_path) + parse_common_project_path = staticmethod(SessionsClient.parse_common_project_path) + + common_location_path = staticmethod(SessionsClient.common_location_path) + parse_common_location_path = staticmethod(SessionsClient.parse_common_location_path) + + from_service_account_file = SessionsClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> SessionsTransport: + """Return the transport used by the client instance. + + Returns: + SessionsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(SessionsClient).get_transport_class, type(SessionsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, SessionsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the sessions 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, ~.SessionsTransport]): 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 = SessionsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def detect_intent( + self, + request: session.DetectIntentRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> session.DetectIntentResponse: + r"""Processes a natural language query and returns + structured, actionable data as a result. This method is + not idempotent, because it may cause session entity + types to be updated, which in turn might affect results + of future queries. + + Args: + request (:class:`~.session.DetectIntentRequest`): + The request object. The request to detect user's intent. + + 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: + ~.session.DetectIntentResponse: + The message returned from the + DetectIntent method. + + """ + # Create or coerce a protobuf request object. + + request = session.DetectIntentRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.detect_intent, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type(exceptions.ServiceUnavailable,), + ), + default_timeout=220.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((("session", request.session),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def streaming_detect_intent( + self, + requests: AsyncIterator[session.StreamingDetectIntentRequest] = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> Awaitable[AsyncIterable[session.StreamingDetectIntentResponse]]: + r"""Processes a natural language query in audio format in + a streaming fashion and returns structured, actionable + data as a result. This method is only available via the + gRPC API (not REST). + + Args: + requests (AsyncIterator[`~.session.StreamingDetectIntentRequest`]): + The request object AsyncIterator. The top-level message sent by the + client to the + [Sessions.StreamingDetectIntent][google.cloud.dialogflow.cx.v3.Sessions.StreamingDetectIntent] + method. + Multiple request messages should be sent in order: + + 1. The first message must contain + [session][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.session], + [query_input][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.query_input] + plus optionally + [query_params][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.query_params]. + If the client wants to receive an audio response, it + should also contain + [output_audio_config][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.output_audio_config]. + 2. If + [query_input][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.query_input] + was set to + [query_input.audio.config][google.cloud.dialogflow.cx.v3.AudioInput.config], + all subsequent messages must contain + [query_input.audio.audio][google.cloud.dialogflow.cx.v3.AudioInput.audio] + to continue with Speech recognition. + If you decide to rather detect an intent from text + input after you already started Speech recognition, + please send a message with + [query_input.text][google.cloud.dialogflow.cx.v3.QueryInput.text]. + However, note that: + + * Dialogflow will bill you for the audio duration so + far. * Dialogflow discards all Speech recognition + results in favor of the input text. + * Dialogflow will use the language code from the + first message. + After you sent all input, you must half-close or abort + the request stream. + 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: + AsyncIterable[~.session.StreamingDetectIntentResponse]: + The top-level message returned from the + ``StreamingDetectIntent`` method. + + Multiple response messages can be returned in order: + + 1. If the input was set to streaming audio, the first + one or more messages contain ``recognition_result``. + Each ``recognition_result`` represents a more + complete transcript of what the user said. The last + ``recognition_result`` has ``is_final`` set to + ``true``. + + 2. The last message contains ``detect_intent_response``. + + """ + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.streaming_detect_intent, + default_timeout=220.0, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Send the request. + response = rpc(requests, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def match_intent( + self, + request: session.MatchIntentRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> session.MatchIntentResponse: + r"""Returns preliminary intent match results, doesn't + change the session status. + + Args: + request (:class:`~.session.MatchIntentRequest`): + The request object. Request of [MatchIntent][]. + + 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: + ~.session.MatchIntentResponse: + Response of [MatchIntent][]. + """ + # Create or coerce a protobuf request object. + + request = session.MatchIntentRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.match_intent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("session", request.session),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def fulfill_intent( + self, + request: session.FulfillIntentRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> session.FulfillIntentResponse: + r"""Fulfills a matched intent returned by + [MatchIntent][google.cloud.dialogflow.cx.v3.Sessions.MatchIntent]. + Must be called after + [MatchIntent][google.cloud.dialogflow.cx.v3.Sessions.MatchIntent], + with input from + [MatchIntentResponse][google.cloud.dialogflow.cx.v3.MatchIntentResponse]. + Otherwise, the behavior is undefined. + + Args: + request (:class:`~.session.FulfillIntentRequest`): + The request object. Request of [FulfillIntent][] + + 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: + ~.session.FulfillIntentResponse: + Response of [FulfillIntent][] + """ + # Create or coerce a protobuf request object. + + request = session.FulfillIntentRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.fulfill_intent, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + ( + ( + "match_intent_request.session", + request.match_intent_request.session, + ), + ) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("SessionsAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/sessions/client.py b/google/cloud/dialogflowcx_v3/services/sessions/client.py new file mode 100644 index 00000000..94988181 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/sessions/client.py @@ -0,0 +1,736 @@ +# -*- 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, + Iterable, + Iterator, + 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.dialogflowcx_v3.types import audio_config +from google.cloud.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import session + +from .transports.base import SessionsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import SessionsGrpcTransport +from .transports.grpc_asyncio import SessionsGrpcAsyncIOTransport + + +class SessionsClientMeta(type): + """Metaclass for the Sessions 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[SessionsTransport]] + _transport_registry["grpc"] = SessionsGrpcTransport + _transport_registry["grpc_asyncio"] = SessionsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[SessionsTransport]: + """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 SessionsClient(metaclass=SessionsClientMeta): + """A session represents an interaction with a user. You retrieve user + input and pass it to the + [DetectIntent][google.cloud.dialogflow.cx.v3.Sessions.DetectIntent] + method to determine user intent and respond. + """ + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> SessionsTransport: + """Return the transport used by the client instance. + + Returns: + SessionsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def entity_type_path( + project: str, location: str, agent: str, entity_type: str, + ) -> str: + """Return a fully-qualified entity_type string.""" + return "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + + @staticmethod + def parse_entity_type_path(path: str) -> Dict[str, str]: + """Parse a entity_type path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/entityTypes/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def flow_path(project: str, location: str, agent: str, flow: str,) -> str: + """Return a fully-qualified flow string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + + @staticmethod + def parse_flow_path(path: str) -> Dict[str, str]: + """Parse a flow path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def intent_path(project: str, location: str, agent: str, intent: str,) -> str: + """Return a fully-qualified intent string.""" + return "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + + @staticmethod + def parse_intent_path(path: str) -> Dict[str, str]: + """Parse a intent path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/intents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def page_path( + project: str, location: str, agent: str, flow: str, page: str, + ) -> str: + """Return a fully-qualified page string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + + @staticmethod + def parse_page_path(path: str) -> Dict[str, str]: + """Parse a page path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/pages/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def session_path(project: str, location: str, agent: str, session: str,) -> str: + """Return a fully-qualified session string.""" + return "projects/{project}/locations/{location}/agents/{agent}/sessions/{session}".format( + project=project, location=location, agent=agent, session=session, + ) + + @staticmethod + def parse_session_path(path: str) -> Dict[str, str]: + """Parse a session path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/sessions/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def session_entity_type_path( + project: str, location: str, agent: str, session: str, entity_type: str, + ) -> str: + """Return a fully-qualified session_entity_type string.""" + return "projects/{project}/locations/{location}/agents/{agent}/sessions/{session}/entityTypes/{entity_type}".format( + project=project, + location=location, + agent=agent, + session=session, + entity_type=entity_type, + ) + + @staticmethod + def parse_session_entity_type_path(path: str) -> Dict[str, str]: + """Parse a session_entity_type path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/sessions/(?P.+?)/entityTypes/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def transition_route_group_path( + project: str, location: str, agent: str, flow: str, transition_route_group: str, + ) -> str: + """Return a fully-qualified transition_route_group string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/transitionRouteGroups/{transition_route_group}".format( + project=project, + location=location, + agent=agent, + flow=flow, + transition_route_group=transition_route_group, + ) + + @staticmethod + def parse_transition_route_group_path(path: str) -> Dict[str, str]: + """Parse a transition_route_group path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/transitionRouteGroups/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def webhook_path(project: str, location: str, agent: str, webhook: str,) -> str: + """Return a fully-qualified webhook string.""" + return "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + + @staticmethod + def parse_webhook_path(path: str) -> Dict[str, str]: + """Parse a webhook path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/webhooks/(?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, SessionsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the sessions 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, ~.SessionsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, SessionsTransport): + # transport is a SessionsTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def detect_intent( + self, + request: session.DetectIntentRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> session.DetectIntentResponse: + r"""Processes a natural language query and returns + structured, actionable data as a result. This method is + not idempotent, because it may cause session entity + types to be updated, which in turn might affect results + of future queries. + + Args: + request (:class:`~.session.DetectIntentRequest`): + The request object. The request to detect user's intent. + + 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: + ~.session.DetectIntentResponse: + The message returned from the + DetectIntent method. + + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a session.DetectIntentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, session.DetectIntentRequest): + request = session.DetectIntentRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.detect_intent] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("session", request.session),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def streaming_detect_intent( + self, + requests: Iterator[session.StreamingDetectIntentRequest] = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> Iterable[session.StreamingDetectIntentResponse]: + r"""Processes a natural language query in audio format in + a streaming fashion and returns structured, actionable + data as a result. This method is only available via the + gRPC API (not REST). + + Args: + requests (Iterator[`~.session.StreamingDetectIntentRequest`]): + The request object iterator. The top-level message sent by the + client to the + [Sessions.StreamingDetectIntent][google.cloud.dialogflow.cx.v3.Sessions.StreamingDetectIntent] + method. + Multiple request messages should be sent in order: + + 1. The first message must contain + [session][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.session], + [query_input][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.query_input] + plus optionally + [query_params][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.query_params]. + If the client wants to receive an audio response, it + should also contain + [output_audio_config][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.output_audio_config]. + 2. If + [query_input][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.query_input] + was set to + [query_input.audio.config][google.cloud.dialogflow.cx.v3.AudioInput.config], + all subsequent messages must contain + [query_input.audio.audio][google.cloud.dialogflow.cx.v3.AudioInput.audio] + to continue with Speech recognition. + If you decide to rather detect an intent from text + input after you already started Speech recognition, + please send a message with + [query_input.text][google.cloud.dialogflow.cx.v3.QueryInput.text]. + However, note that: + + * Dialogflow will bill you for the audio duration so + far. * Dialogflow discards all Speech recognition + results in favor of the input text. + * Dialogflow will use the language code from the + first message. + After you sent all input, you must half-close or abort + the request stream. + 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: + Iterable[~.session.StreamingDetectIntentResponse]: + The top-level message returned from the + ``StreamingDetectIntent`` method. + + Multiple response messages can be returned in order: + + 1. If the input was set to streaming audio, the first + one or more messages contain ``recognition_result``. + Each ``recognition_result`` represents a more + complete transcript of what the user said. The last + ``recognition_result`` has ``is_final`` set to + ``true``. + + 2. The last message contains ``detect_intent_response``. + + """ + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.streaming_detect_intent] + + # Send the request. + response = rpc(requests, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def match_intent( + self, + request: session.MatchIntentRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> session.MatchIntentResponse: + r"""Returns preliminary intent match results, doesn't + change the session status. + + Args: + request (:class:`~.session.MatchIntentRequest`): + The request object. Request of [MatchIntent][]. + + 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: + ~.session.MatchIntentResponse: + Response of [MatchIntent][]. + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a session.MatchIntentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, session.MatchIntentRequest): + request = session.MatchIntentRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.match_intent] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("session", request.session),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def fulfill_intent( + self, + request: session.FulfillIntentRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> session.FulfillIntentResponse: + r"""Fulfills a matched intent returned by + [MatchIntent][google.cloud.dialogflow.cx.v3.Sessions.MatchIntent]. + Must be called after + [MatchIntent][google.cloud.dialogflow.cx.v3.Sessions.MatchIntent], + with input from + [MatchIntentResponse][google.cloud.dialogflow.cx.v3.MatchIntentResponse]. + Otherwise, the behavior is undefined. + + Args: + request (:class:`~.session.FulfillIntentRequest`): + The request object. Request of [FulfillIntent][] + + 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: + ~.session.FulfillIntentResponse: + Response of [FulfillIntent][] + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a session.FulfillIntentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, session.FulfillIntentRequest): + request = session.FulfillIntentRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.fulfill_intent] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + ( + ( + "match_intent_request.session", + request.match_intent_request.session, + ), + ) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("SessionsClient",) diff --git a/google/cloud/dialogflowcx_v3/services/sessions/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/sessions/transports/__init__.py new file mode 100644 index 00000000..1e1b2a5e --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/sessions/transports/__init__.py @@ -0,0 +1,36 @@ +# -*- 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 SessionsTransport +from .grpc import SessionsGrpcTransport +from .grpc_asyncio import SessionsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[SessionsTransport]] +_transport_registry["grpc"] = SessionsGrpcTransport +_transport_registry["grpc_asyncio"] = SessionsGrpcAsyncIOTransport + + +__all__ = ( + "SessionsTransport", + "SessionsGrpcTransport", + "SessionsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/sessions/transports/base.py b/google/cloud/dialogflowcx_v3/services/sessions/transports/base.py new file mode 100644 index 00000000..410fe8c0 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/sessions/transports/base.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import 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.dialogflowcx_v3.types import session + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class SessionsTransport(abc.ABC): + """Abstract transport class for Sessions.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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.detect_intent: gapic_v1.method.wrap_method( + self.detect_intent, + default_retry=retries.Retry( + initial=0.1, + maximum=60.0, + multiplier=1.3, + predicate=retries.if_exception_type(exceptions.ServiceUnavailable,), + ), + default_timeout=220.0, + client_info=client_info, + ), + self.streaming_detect_intent: gapic_v1.method.wrap_method( + self.streaming_detect_intent, + default_timeout=220.0, + client_info=client_info, + ), + self.match_intent: gapic_v1.method.wrap_method( + self.match_intent, default_timeout=None, client_info=client_info, + ), + self.fulfill_intent: gapic_v1.method.wrap_method( + self.fulfill_intent, default_timeout=None, client_info=client_info, + ), + } + + @property + def detect_intent( + self, + ) -> typing.Callable[ + [session.DetectIntentRequest], + typing.Union[ + session.DetectIntentResponse, typing.Awaitable[session.DetectIntentResponse] + ], + ]: + raise NotImplementedError() + + @property + def streaming_detect_intent( + self, + ) -> typing.Callable[ + [session.StreamingDetectIntentRequest], + typing.Union[ + session.StreamingDetectIntentResponse, + typing.Awaitable[session.StreamingDetectIntentResponse], + ], + ]: + raise NotImplementedError() + + @property + def match_intent( + self, + ) -> typing.Callable[ + [session.MatchIntentRequest], + typing.Union[ + session.MatchIntentResponse, typing.Awaitable[session.MatchIntentResponse] + ], + ]: + raise NotImplementedError() + + @property + def fulfill_intent( + self, + ) -> typing.Callable[ + [session.FulfillIntentRequest], + typing.Union[ + session.FulfillIntentResponse, + typing.Awaitable[session.FulfillIntentResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("SessionsTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/sessions/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/sessions/transports/grpc.py new file mode 100644 index 00000000..5922ab35 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/sessions/transports/grpc.py @@ -0,0 +1,356 @@ +# -*- 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.dialogflowcx_v3.types import session + +from .base import SessionsTransport, DEFAULT_CLIENT_INFO + + +class SessionsGrpcTransport(SessionsTransport): + """gRPC backend transport for Sessions. + + A session represents an interaction with a user. You retrieve user + input and pass it to the + [DetectIntent][google.cloud.dialogflow.cx.v3.Sessions.DetectIntent] + method to determine user intent and respond. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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 detect_intent( + self, + ) -> Callable[[session.DetectIntentRequest], session.DetectIntentResponse]: + r"""Return a callable for the detect intent method over gRPC. + + Processes a natural language query and returns + structured, actionable data as a result. This method is + not idempotent, because it may cause session entity + types to be updated, which in turn might affect results + of future queries. + + Returns: + Callable[[~.DetectIntentRequest], + ~.DetectIntentResponse]: + 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 "detect_intent" not in self._stubs: + self._stubs["detect_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Sessions/DetectIntent", + request_serializer=session.DetectIntentRequest.serialize, + response_deserializer=session.DetectIntentResponse.deserialize, + ) + return self._stubs["detect_intent"] + + @property + def streaming_detect_intent( + self, + ) -> Callable[ + [session.StreamingDetectIntentRequest], session.StreamingDetectIntentResponse + ]: + r"""Return a callable for the streaming detect intent method over gRPC. + + Processes a natural language query in audio format in + a streaming fashion and returns structured, actionable + data as a result. This method is only available via the + gRPC API (not REST). + + Returns: + Callable[[~.StreamingDetectIntentRequest], + ~.StreamingDetectIntentResponse]: + 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 "streaming_detect_intent" not in self._stubs: + self._stubs["streaming_detect_intent"] = self.grpc_channel.stream_stream( + "/google.cloud.dialogflow.cx.v3.Sessions/StreamingDetectIntent", + request_serializer=session.StreamingDetectIntentRequest.serialize, + response_deserializer=session.StreamingDetectIntentResponse.deserialize, + ) + return self._stubs["streaming_detect_intent"] + + @property + def match_intent( + self, + ) -> Callable[[session.MatchIntentRequest], session.MatchIntentResponse]: + r"""Return a callable for the match intent method over gRPC. + + Returns preliminary intent match results, doesn't + change the session status. + + Returns: + Callable[[~.MatchIntentRequest], + ~.MatchIntentResponse]: + 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 "match_intent" not in self._stubs: + self._stubs["match_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Sessions/MatchIntent", + request_serializer=session.MatchIntentRequest.serialize, + response_deserializer=session.MatchIntentResponse.deserialize, + ) + return self._stubs["match_intent"] + + @property + def fulfill_intent( + self, + ) -> Callable[[session.FulfillIntentRequest], session.FulfillIntentResponse]: + r"""Return a callable for the fulfill intent method over gRPC. + + Fulfills a matched intent returned by + [MatchIntent][google.cloud.dialogflow.cx.v3.Sessions.MatchIntent]. + Must be called after + [MatchIntent][google.cloud.dialogflow.cx.v3.Sessions.MatchIntent], + with input from + [MatchIntentResponse][google.cloud.dialogflow.cx.v3.MatchIntentResponse]. + Otherwise, the behavior is undefined. + + Returns: + Callable[[~.FulfillIntentRequest], + ~.FulfillIntentResponse]: + 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 "fulfill_intent" not in self._stubs: + self._stubs["fulfill_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Sessions/FulfillIntent", + request_serializer=session.FulfillIntentRequest.serialize, + response_deserializer=session.FulfillIntentResponse.deserialize, + ) + return self._stubs["fulfill_intent"] + + +__all__ = ("SessionsGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/sessions/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/sessions/transports/grpc_asyncio.py new file mode 100644 index 00000000..e6c062f6 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/sessions/transports/grpc_asyncio.py @@ -0,0 +1,365 @@ +# -*- 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.dialogflowcx_v3.types import session + +from .base import SessionsTransport, DEFAULT_CLIENT_INFO +from .grpc import SessionsGrpcTransport + + +class SessionsGrpcAsyncIOTransport(SessionsTransport): + """gRPC AsyncIO backend transport for Sessions. + + A session represents an interaction with a user. You retrieve user + input and pass it to the + [DetectIntent][google.cloud.dialogflow.cx.v3.Sessions.DetectIntent] + method to determine user intent and respond. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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 detect_intent( + self, + ) -> Callable[ + [session.DetectIntentRequest], Awaitable[session.DetectIntentResponse] + ]: + r"""Return a callable for the detect intent method over gRPC. + + Processes a natural language query and returns + structured, actionable data as a result. This method is + not idempotent, because it may cause session entity + types to be updated, which in turn might affect results + of future queries. + + Returns: + Callable[[~.DetectIntentRequest], + Awaitable[~.DetectIntentResponse]]: + 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 "detect_intent" not in self._stubs: + self._stubs["detect_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Sessions/DetectIntent", + request_serializer=session.DetectIntentRequest.serialize, + response_deserializer=session.DetectIntentResponse.deserialize, + ) + return self._stubs["detect_intent"] + + @property + def streaming_detect_intent( + self, + ) -> Callable[ + [session.StreamingDetectIntentRequest], + Awaitable[session.StreamingDetectIntentResponse], + ]: + r"""Return a callable for the streaming detect intent method over gRPC. + + Processes a natural language query in audio format in + a streaming fashion and returns structured, actionable + data as a result. This method is only available via the + gRPC API (not REST). + + Returns: + Callable[[~.StreamingDetectIntentRequest], + Awaitable[~.StreamingDetectIntentResponse]]: + 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 "streaming_detect_intent" not in self._stubs: + self._stubs["streaming_detect_intent"] = self.grpc_channel.stream_stream( + "/google.cloud.dialogflow.cx.v3.Sessions/StreamingDetectIntent", + request_serializer=session.StreamingDetectIntentRequest.serialize, + response_deserializer=session.StreamingDetectIntentResponse.deserialize, + ) + return self._stubs["streaming_detect_intent"] + + @property + def match_intent( + self, + ) -> Callable[[session.MatchIntentRequest], Awaitable[session.MatchIntentResponse]]: + r"""Return a callable for the match intent method over gRPC. + + Returns preliminary intent match results, doesn't + change the session status. + + Returns: + Callable[[~.MatchIntentRequest], + Awaitable[~.MatchIntentResponse]]: + 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 "match_intent" not in self._stubs: + self._stubs["match_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Sessions/MatchIntent", + request_serializer=session.MatchIntentRequest.serialize, + response_deserializer=session.MatchIntentResponse.deserialize, + ) + return self._stubs["match_intent"] + + @property + def fulfill_intent( + self, + ) -> Callable[ + [session.FulfillIntentRequest], Awaitable[session.FulfillIntentResponse] + ]: + r"""Return a callable for the fulfill intent method over gRPC. + + Fulfills a matched intent returned by + [MatchIntent][google.cloud.dialogflow.cx.v3.Sessions.MatchIntent]. + Must be called after + [MatchIntent][google.cloud.dialogflow.cx.v3.Sessions.MatchIntent], + with input from + [MatchIntentResponse][google.cloud.dialogflow.cx.v3.MatchIntentResponse]. + Otherwise, the behavior is undefined. + + Returns: + Callable[[~.FulfillIntentRequest], + Awaitable[~.FulfillIntentResponse]]: + 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 "fulfill_intent" not in self._stubs: + self._stubs["fulfill_intent"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Sessions/FulfillIntent", + request_serializer=session.FulfillIntentRequest.serialize, + response_deserializer=session.FulfillIntentResponse.deserialize, + ) + return self._stubs["fulfill_intent"] + + +__all__ = ("SessionsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/transition_route_groups/__init__.py b/google/cloud/dialogflowcx_v3/services/transition_route_groups/__init__.py new file mode 100644 index 00000000..af48a31a --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/transition_route_groups/__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 TransitionRouteGroupsClient +from .async_client import TransitionRouteGroupsAsyncClient + +__all__ = ( + "TransitionRouteGroupsClient", + "TransitionRouteGroupsAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/transition_route_groups/async_client.py b/google/cloud/dialogflowcx_v3/services/transition_route_groups/async_client.py new file mode 100644 index 00000000..dcd39a75 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/transition_route_groups/async_client.py @@ -0,0 +1,577 @@ +# -*- 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.dialogflowcx_v3.services.transition_route_groups import pagers +from google.cloud.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import transition_route_group +from google.cloud.dialogflowcx_v3.types import ( + transition_route_group as gcdc_transition_route_group, +) +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import TransitionRouteGroupsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import TransitionRouteGroupsGrpcAsyncIOTransport +from .client import TransitionRouteGroupsClient + + +class TransitionRouteGroupsAsyncClient: + """Service for managing + [TransitionRouteGroups][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + """ + + _client: TransitionRouteGroupsClient + + DEFAULT_ENDPOINT = TransitionRouteGroupsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = TransitionRouteGroupsClient.DEFAULT_MTLS_ENDPOINT + + flow_path = staticmethod(TransitionRouteGroupsClient.flow_path) + parse_flow_path = staticmethod(TransitionRouteGroupsClient.parse_flow_path) + intent_path = staticmethod(TransitionRouteGroupsClient.intent_path) + parse_intent_path = staticmethod(TransitionRouteGroupsClient.parse_intent_path) + page_path = staticmethod(TransitionRouteGroupsClient.page_path) + parse_page_path = staticmethod(TransitionRouteGroupsClient.parse_page_path) + transition_route_group_path = staticmethod( + TransitionRouteGroupsClient.transition_route_group_path + ) + parse_transition_route_group_path = staticmethod( + TransitionRouteGroupsClient.parse_transition_route_group_path + ) + webhook_path = staticmethod(TransitionRouteGroupsClient.webhook_path) + parse_webhook_path = staticmethod(TransitionRouteGroupsClient.parse_webhook_path) + + common_billing_account_path = staticmethod( + TransitionRouteGroupsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + TransitionRouteGroupsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(TransitionRouteGroupsClient.common_folder_path) + parse_common_folder_path = staticmethod( + TransitionRouteGroupsClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + TransitionRouteGroupsClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + TransitionRouteGroupsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(TransitionRouteGroupsClient.common_project_path) + parse_common_project_path = staticmethod( + TransitionRouteGroupsClient.parse_common_project_path + ) + + common_location_path = staticmethod( + TransitionRouteGroupsClient.common_location_path + ) + parse_common_location_path = staticmethod( + TransitionRouteGroupsClient.parse_common_location_path + ) + + from_service_account_file = TransitionRouteGroupsClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> TransitionRouteGroupsTransport: + """Return the transport used by the client instance. + + Returns: + TransitionRouteGroupsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(TransitionRouteGroupsClient).get_transport_class, + type(TransitionRouteGroupsClient), + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, TransitionRouteGroupsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transition route groups 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, ~.TransitionRouteGroupsTransport]): 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 = TransitionRouteGroupsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_transition_route_groups( + self, + request: transition_route_group.ListTransitionRouteGroupsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListTransitionRouteGroupsAsyncPager: + r"""Returns the list of all transition route groups in + the specified flow. + + Args: + request (:class:`~.transition_route_group.ListTransitionRouteGroupsRequest`): + The request object. The request message for + [TransitionRouteGroups.ListTransitionRouteGroups][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.ListTransitionRouteGroups]. + parent (:class:`str`): + Required. The flow to list all transition route groups + for. Format: + ``projects//locations//agents//flows/``. + 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: + ~.pagers.ListTransitionRouteGroupsAsyncPager: + The response message for + [TransitionRouteGroups.ListTransitionRouteGroups][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.ListTransitionRouteGroups]. + + 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 = transition_route_group.ListTransitionRouteGroupsRequest(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_transition_route_groups, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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.ListTransitionRouteGroupsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_transition_route_group( + self, + request: transition_route_group.GetTransitionRouteGroupRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> transition_route_group.TransitionRouteGroup: + r"""Retrieves the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Args: + request (:class:`~.transition_route_group.GetTransitionRouteGroupRequest`): + The request object. The request message for + [TransitionRouteGroups.GetTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.GetTransitionRouteGroup]. + name (:class:`str`): + Required. The name of the + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + Format: + ``projects//locations//agents//flows//transitionRouteGroups/``. + 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: + ~.transition_route_group.TransitionRouteGroup: + An TransitionRouteGroup represents a group of + [``TransitionRoutes``][google.cloud.dialogflow.cx.v3.TransitionRoute] + to be used by a + [Page][google.cloud.dialogflow.cx.v3.Page]. + + """ + # 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 = transition_route_group.GetTransitionRouteGroupRequest(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_transition_route_group, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_transition_route_group( + self, + request: gcdc_transition_route_group.CreateTransitionRouteGroupRequest = None, + *, + parent: str = None, + transition_route_group: gcdc_transition_route_group.TransitionRouteGroup = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_transition_route_group.TransitionRouteGroup: + r"""Creates an + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup] + in the specified flow. + + Args: + request (:class:`~.gcdc_transition_route_group.CreateTransitionRouteGroupRequest`): + The request object. The request message for + [TransitionRouteGroups.CreateTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.CreateTransitionRouteGroup]. + parent (:class:`str`): + Required. The flow to create an + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup] + for. Format: + ``projects//locations//agents//flows/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + transition_route_group (:class:`~.gcdc_transition_route_group.TransitionRouteGroup`): + Required. The transition route group + to create. + This corresponds to the ``transition_route_group`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + 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: + ~.gcdc_transition_route_group.TransitionRouteGroup: + An TransitionRouteGroup represents a group of + [``TransitionRoutes``][google.cloud.dialogflow.cx.v3.TransitionRoute] + to be used by a + [Page][google.cloud.dialogflow.cx.v3.Page]. + + """ + # 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, transition_route_group]) + 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 = gcdc_transition_route_group.CreateTransitionRouteGroupRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if transition_route_group is not None: + request.transition_route_group = transition_route_group + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.create_transition_route_group, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_transition_route_group( + self, + request: gcdc_transition_route_group.UpdateTransitionRouteGroupRequest = None, + *, + transition_route_group: gcdc_transition_route_group.TransitionRouteGroup = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_transition_route_group.TransitionRouteGroup: + r"""Updates the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Args: + request (:class:`~.gcdc_transition_route_group.UpdateTransitionRouteGroupRequest`): + The request object. The request message for + [TransitionRouteGroups.UpdateTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.UpdateTransitionRouteGroup]. + transition_route_group (:class:`~.gcdc_transition_route_group.TransitionRouteGroup`): + Required. The transition route group + to update. + This corresponds to the ``transition_route_group`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_transition_route_group.TransitionRouteGroup: + An TransitionRouteGroup represents a group of + [``TransitionRoutes``][google.cloud.dialogflow.cx.v3.TransitionRoute] + to be used by a + [Page][google.cloud.dialogflow.cx.v3.Page]. + + """ + # 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([transition_route_group, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = gcdc_transition_route_group.UpdateTransitionRouteGroupRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if transition_route_group is not None: + request.transition_route_group = transition_route_group + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_transition_route_group, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("transition_route_group.name", request.transition_route_group.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_transition_route_group( + self, + request: transition_route_group.DeleteTransitionRouteGroupRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Args: + request (:class:`~.transition_route_group.DeleteTransitionRouteGroupRequest`): + The request object. The request message for + [TransitionRouteGroups.DeleteTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.DeleteTransitionRouteGroup]. + name (:class:`str`): + Required. The name of the + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup] + to delete. Format: + ``projects//locations//agents//flows//transitionRouteGroups/``. + 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 = transition_route_group.DeleteTransitionRouteGroupRequest(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_transition_route_group, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TransitionRouteGroupsAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/transition_route_groups/client.py b/google/cloud/dialogflowcx_v3/services/transition_route_groups/client.py new file mode 100644 index 00000000..2b627e9f --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/transition_route_groups/client.py @@ -0,0 +1,856 @@ +# -*- 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.dialogflowcx_v3.services.transition_route_groups import pagers +from google.cloud.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import transition_route_group +from google.cloud.dialogflowcx_v3.types import ( + transition_route_group as gcdc_transition_route_group, +) +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import TransitionRouteGroupsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import TransitionRouteGroupsGrpcTransport +from .transports.grpc_asyncio import TransitionRouteGroupsGrpcAsyncIOTransport + + +class TransitionRouteGroupsClientMeta(type): + """Metaclass for the TransitionRouteGroups 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[TransitionRouteGroupsTransport]] + _transport_registry["grpc"] = TransitionRouteGroupsGrpcTransport + _transport_registry["grpc_asyncio"] = TransitionRouteGroupsGrpcAsyncIOTransport + + def get_transport_class( + cls, label: str = None, + ) -> Type[TransitionRouteGroupsTransport]: + """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 TransitionRouteGroupsClient(metaclass=TransitionRouteGroupsClientMeta): + """Service for managing + [TransitionRouteGroups][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + """ + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> TransitionRouteGroupsTransport: + """Return the transport used by the client instance. + + Returns: + TransitionRouteGroupsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def flow_path(project: str, location: str, agent: str, flow: str,) -> str: + """Return a fully-qualified flow string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + + @staticmethod + def parse_flow_path(path: str) -> Dict[str, str]: + """Parse a flow path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def intent_path(project: str, location: str, agent: str, intent: str,) -> str: + """Return a fully-qualified intent string.""" + return "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + + @staticmethod + def parse_intent_path(path: str) -> Dict[str, str]: + """Parse a intent path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/intents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def page_path( + project: str, location: str, agent: str, flow: str, page: str, + ) -> str: + """Return a fully-qualified page string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + + @staticmethod + def parse_page_path(path: str) -> Dict[str, str]: + """Parse a page path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/pages/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def transition_route_group_path( + project: str, location: str, agent: str, flow: str, transition_route_group: str, + ) -> str: + """Return a fully-qualified transition_route_group string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/transitionRouteGroups/{transition_route_group}".format( + project=project, + location=location, + agent=agent, + flow=flow, + transition_route_group=transition_route_group, + ) + + @staticmethod + def parse_transition_route_group_path(path: str) -> Dict[str, str]: + """Parse a transition_route_group path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/transitionRouteGroups/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def webhook_path(project: str, location: str, agent: str, webhook: str,) -> str: + """Return a fully-qualified webhook string.""" + return "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + + @staticmethod + def parse_webhook_path(path: str) -> Dict[str, str]: + """Parse a webhook path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/webhooks/(?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, TransitionRouteGroupsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transition route groups 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, ~.TransitionRouteGroupsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, TransitionRouteGroupsTransport): + # transport is a TransitionRouteGroupsTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_transition_route_groups( + self, + request: transition_route_group.ListTransitionRouteGroupsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListTransitionRouteGroupsPager: + r"""Returns the list of all transition route groups in + the specified flow. + + Args: + request (:class:`~.transition_route_group.ListTransitionRouteGroupsRequest`): + The request object. The request message for + [TransitionRouteGroups.ListTransitionRouteGroups][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.ListTransitionRouteGroups]. + parent (:class:`str`): + Required. The flow to list all transition route groups + for. Format: + ``projects//locations//agents//flows/``. + 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: + ~.pagers.ListTransitionRouteGroupsPager: + The response message for + [TransitionRouteGroups.ListTransitionRouteGroups][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.ListTransitionRouteGroups]. + + 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 transition_route_group.ListTransitionRouteGroupsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, transition_route_group.ListTransitionRouteGroupsRequest + ): + request = transition_route_group.ListTransitionRouteGroupsRequest(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_transition_route_groups + ] + + # 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.ListTransitionRouteGroupsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_transition_route_group( + self, + request: transition_route_group.GetTransitionRouteGroupRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> transition_route_group.TransitionRouteGroup: + r"""Retrieves the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Args: + request (:class:`~.transition_route_group.GetTransitionRouteGroupRequest`): + The request object. The request message for + [TransitionRouteGroups.GetTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.GetTransitionRouteGroup]. + name (:class:`str`): + Required. The name of the + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + Format: + ``projects//locations//agents//flows//transitionRouteGroups/``. + 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: + ~.transition_route_group.TransitionRouteGroup: + An TransitionRouteGroup represents a group of + [``TransitionRoutes``][google.cloud.dialogflow.cx.v3.TransitionRoute] + to be used by a + [Page][google.cloud.dialogflow.cx.v3.Page]. + + """ + # 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 transition_route_group.GetTransitionRouteGroupRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, transition_route_group.GetTransitionRouteGroupRequest + ): + request = transition_route_group.GetTransitionRouteGroupRequest(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_transition_route_group + ] + + # 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_transition_route_group( + self, + request: gcdc_transition_route_group.CreateTransitionRouteGroupRequest = None, + *, + parent: str = None, + transition_route_group: gcdc_transition_route_group.TransitionRouteGroup = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_transition_route_group.TransitionRouteGroup: + r"""Creates an + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup] + in the specified flow. + + Args: + request (:class:`~.gcdc_transition_route_group.CreateTransitionRouteGroupRequest`): + The request object. The request message for + [TransitionRouteGroups.CreateTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.CreateTransitionRouteGroup]. + parent (:class:`str`): + Required. The flow to create an + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup] + for. Format: + ``projects//locations//agents//flows/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + transition_route_group (:class:`~.gcdc_transition_route_group.TransitionRouteGroup`): + Required. The transition route group + to create. + This corresponds to the ``transition_route_group`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + 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: + ~.gcdc_transition_route_group.TransitionRouteGroup: + An TransitionRouteGroup represents a group of + [``TransitionRoutes``][google.cloud.dialogflow.cx.v3.TransitionRoute] + to be used by a + [Page][google.cloud.dialogflow.cx.v3.Page]. + + """ + # 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, transition_route_group]) + 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 gcdc_transition_route_group.CreateTransitionRouteGroupRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, gcdc_transition_route_group.CreateTransitionRouteGroupRequest + ): + request = gcdc_transition_route_group.CreateTransitionRouteGroupRequest( + request + ) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if transition_route_group is not None: + request.transition_route_group = transition_route_group + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[ + self._transport.create_transition_route_group + ] + + # 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_transition_route_group( + self, + request: gcdc_transition_route_group.UpdateTransitionRouteGroupRequest = None, + *, + transition_route_group: gcdc_transition_route_group.TransitionRouteGroup = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_transition_route_group.TransitionRouteGroup: + r"""Updates the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Args: + request (:class:`~.gcdc_transition_route_group.UpdateTransitionRouteGroupRequest`): + The request object. The request message for + [TransitionRouteGroups.UpdateTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.UpdateTransitionRouteGroup]. + transition_route_group (:class:`~.gcdc_transition_route_group.TransitionRouteGroup`): + Required. The transition route group + to update. + This corresponds to the ``transition_route_group`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_transition_route_group.TransitionRouteGroup: + An TransitionRouteGroup represents a group of + [``TransitionRoutes``][google.cloud.dialogflow.cx.v3.TransitionRoute] + to be used by a + [Page][google.cloud.dialogflow.cx.v3.Page]. + + """ + # 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([transition_route_group, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a gcdc_transition_route_group.UpdateTransitionRouteGroupRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, gcdc_transition_route_group.UpdateTransitionRouteGroupRequest + ): + request = gcdc_transition_route_group.UpdateTransitionRouteGroupRequest( + request + ) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if transition_route_group is not None: + request.transition_route_group = transition_route_group + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[ + self._transport.update_transition_route_group + ] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("transition_route_group.name", request.transition_route_group.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def delete_transition_route_group( + self, + request: transition_route_group.DeleteTransitionRouteGroupRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Args: + request (:class:`~.transition_route_group.DeleteTransitionRouteGroupRequest`): + The request object. The request message for + [TransitionRouteGroups.DeleteTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.DeleteTransitionRouteGroup]. + name (:class:`str`): + Required. The name of the + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup] + to delete. Format: + ``projects//locations//agents//flows//transitionRouteGroups/``. + 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 transition_route_group.DeleteTransitionRouteGroupRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, transition_route_group.DeleteTransitionRouteGroupRequest + ): + request = transition_route_group.DeleteTransitionRouteGroupRequest(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_transition_route_group + ] + + # 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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("TransitionRouteGroupsClient",) diff --git a/google/cloud/dialogflowcx_v3/services/transition_route_groups/pagers.py b/google/cloud/dialogflowcx_v3/services/transition_route_groups/pagers.py new file mode 100644 index 00000000..0326841d --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/transition_route_groups/pagers.py @@ -0,0 +1,154 @@ +# -*- 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 + +from google.cloud.dialogflowcx_v3.types import transition_route_group + + +class ListTransitionRouteGroupsPager: + """A pager for iterating through ``list_transition_route_groups`` requests. + + This class thinly wraps an initial + :class:`~.transition_route_group.ListTransitionRouteGroupsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``transition_route_groups`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListTransitionRouteGroups`` requests and continue to iterate + through the ``transition_route_groups`` field on the + corresponding responses. + + All the usual :class:`~.transition_route_group.ListTransitionRouteGroupsResponse` + 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[..., transition_route_group.ListTransitionRouteGroupsResponse], + request: transition_route_group.ListTransitionRouteGroupsRequest, + response: transition_route_group.ListTransitionRouteGroupsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.transition_route_group.ListTransitionRouteGroupsRequest`): + The initial request object. + response (:class:`~.transition_route_group.ListTransitionRouteGroupsResponse`): + 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 = transition_route_group.ListTransitionRouteGroupsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages( + self, + ) -> Iterable[transition_route_group.ListTransitionRouteGroupsResponse]: + 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[transition_route_group.TransitionRouteGroup]: + for page in self.pages: + yield from page.transition_route_groups + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListTransitionRouteGroupsAsyncPager: + """A pager for iterating through ``list_transition_route_groups`` requests. + + This class thinly wraps an initial + :class:`~.transition_route_group.ListTransitionRouteGroupsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``transition_route_groups`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListTransitionRouteGroups`` requests and continue to iterate + through the ``transition_route_groups`` field on the + corresponding responses. + + All the usual :class:`~.transition_route_group.ListTransitionRouteGroupsResponse` + 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[transition_route_group.ListTransitionRouteGroupsResponse] + ], + request: transition_route_group.ListTransitionRouteGroupsRequest, + response: transition_route_group.ListTransitionRouteGroupsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.transition_route_group.ListTransitionRouteGroupsRequest`): + The initial request object. + response (:class:`~.transition_route_group.ListTransitionRouteGroupsResponse`): + 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 = transition_route_group.ListTransitionRouteGroupsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages( + self, + ) -> AsyncIterable[transition_route_group.ListTransitionRouteGroupsResponse]: + 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[transition_route_group.TransitionRouteGroup]: + async def async_generator(): + async for page in self.pages: + for response in page.transition_route_groups: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/__init__.py new file mode 100644 index 00000000..36c1a1bc --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/__init__.py @@ -0,0 +1,38 @@ +# -*- 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 TransitionRouteGroupsTransport +from .grpc import TransitionRouteGroupsGrpcTransport +from .grpc_asyncio import TransitionRouteGroupsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = ( + OrderedDict() +) # type: Dict[str, Type[TransitionRouteGroupsTransport]] +_transport_registry["grpc"] = TransitionRouteGroupsGrpcTransport +_transport_registry["grpc_asyncio"] = TransitionRouteGroupsGrpcAsyncIOTransport + + +__all__ = ( + "TransitionRouteGroupsTransport", + "TransitionRouteGroupsGrpcTransport", + "TransitionRouteGroupsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/base.py b/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/base.py new file mode 100644 index 00000000..8d8ec683 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/base.py @@ -0,0 +1,201 @@ +# -*- 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.dialogflowcx_v3.types import transition_route_group +from google.cloud.dialogflowcx_v3.types import ( + transition_route_group as gcdc_transition_route_group, +) +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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class TransitionRouteGroupsTransport(abc.ABC): + """Abstract transport class for TransitionRouteGroups.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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_transition_route_groups: gapic_v1.method.wrap_method( + self.list_transition_route_groups, + default_timeout=None, + client_info=client_info, + ), + self.get_transition_route_group: gapic_v1.method.wrap_method( + self.get_transition_route_group, + default_timeout=None, + client_info=client_info, + ), + self.create_transition_route_group: gapic_v1.method.wrap_method( + self.create_transition_route_group, + default_timeout=None, + client_info=client_info, + ), + self.update_transition_route_group: gapic_v1.method.wrap_method( + self.update_transition_route_group, + default_timeout=None, + client_info=client_info, + ), + self.delete_transition_route_group: gapic_v1.method.wrap_method( + self.delete_transition_route_group, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def list_transition_route_groups( + self, + ) -> typing.Callable[ + [transition_route_group.ListTransitionRouteGroupsRequest], + typing.Union[ + transition_route_group.ListTransitionRouteGroupsResponse, + typing.Awaitable[transition_route_group.ListTransitionRouteGroupsResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_transition_route_group( + self, + ) -> typing.Callable[ + [transition_route_group.GetTransitionRouteGroupRequest], + typing.Union[ + transition_route_group.TransitionRouteGroup, + typing.Awaitable[transition_route_group.TransitionRouteGroup], + ], + ]: + raise NotImplementedError() + + @property + def create_transition_route_group( + self, + ) -> typing.Callable[ + [gcdc_transition_route_group.CreateTransitionRouteGroupRequest], + typing.Union[ + gcdc_transition_route_group.TransitionRouteGroup, + typing.Awaitable[gcdc_transition_route_group.TransitionRouteGroup], + ], + ]: + raise NotImplementedError() + + @property + def update_transition_route_group( + self, + ) -> typing.Callable[ + [gcdc_transition_route_group.UpdateTransitionRouteGroupRequest], + typing.Union[ + gcdc_transition_route_group.TransitionRouteGroup, + typing.Awaitable[gcdc_transition_route_group.TransitionRouteGroup], + ], + ]: + raise NotImplementedError() + + @property + def delete_transition_route_group( + self, + ) -> typing.Callable[ + [transition_route_group.DeleteTransitionRouteGroupRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + +__all__ = ("TransitionRouteGroupsTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/grpc.py new file mode 100644 index 00000000..917e74f8 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/grpc.py @@ -0,0 +1,394 @@ +# -*- 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.dialogflowcx_v3.types import transition_route_group +from google.cloud.dialogflowcx_v3.types import ( + transition_route_group as gcdc_transition_route_group, +) +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import TransitionRouteGroupsTransport, DEFAULT_CLIENT_INFO + + +class TransitionRouteGroupsGrpcTransport(TransitionRouteGroupsTransport): + """gRPC backend transport for TransitionRouteGroups. + + Service for managing + [TransitionRouteGroups][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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_transition_route_groups( + self, + ) -> Callable[ + [transition_route_group.ListTransitionRouteGroupsRequest], + transition_route_group.ListTransitionRouteGroupsResponse, + ]: + r"""Return a callable for the list transition route groups method over gRPC. + + Returns the list of all transition route groups in + the specified flow. + + Returns: + Callable[[~.ListTransitionRouteGroupsRequest], + ~.ListTransitionRouteGroupsResponse]: + 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_transition_route_groups" not in self._stubs: + self._stubs["list_transition_route_groups"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.TransitionRouteGroups/ListTransitionRouteGroups", + request_serializer=transition_route_group.ListTransitionRouteGroupsRequest.serialize, + response_deserializer=transition_route_group.ListTransitionRouteGroupsResponse.deserialize, + ) + return self._stubs["list_transition_route_groups"] + + @property + def get_transition_route_group( + self, + ) -> Callable[ + [transition_route_group.GetTransitionRouteGroupRequest], + transition_route_group.TransitionRouteGroup, + ]: + r"""Return a callable for the get transition route group method over gRPC. + + Retrieves the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Returns: + Callable[[~.GetTransitionRouteGroupRequest], + ~.TransitionRouteGroup]: + 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_transition_route_group" not in self._stubs: + self._stubs["get_transition_route_group"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.TransitionRouteGroups/GetTransitionRouteGroup", + request_serializer=transition_route_group.GetTransitionRouteGroupRequest.serialize, + response_deserializer=transition_route_group.TransitionRouteGroup.deserialize, + ) + return self._stubs["get_transition_route_group"] + + @property + def create_transition_route_group( + self, + ) -> Callable[ + [gcdc_transition_route_group.CreateTransitionRouteGroupRequest], + gcdc_transition_route_group.TransitionRouteGroup, + ]: + r"""Return a callable for the create transition route group method over gRPC. + + Creates an + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup] + in the specified flow. + + Returns: + Callable[[~.CreateTransitionRouteGroupRequest], + ~.TransitionRouteGroup]: + 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_transition_route_group" not in self._stubs: + self._stubs[ + "create_transition_route_group" + ] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.TransitionRouteGroups/CreateTransitionRouteGroup", + request_serializer=gcdc_transition_route_group.CreateTransitionRouteGroupRequest.serialize, + response_deserializer=gcdc_transition_route_group.TransitionRouteGroup.deserialize, + ) + return self._stubs["create_transition_route_group"] + + @property + def update_transition_route_group( + self, + ) -> Callable[ + [gcdc_transition_route_group.UpdateTransitionRouteGroupRequest], + gcdc_transition_route_group.TransitionRouteGroup, + ]: + r"""Return a callable for the update transition route group method over gRPC. + + Updates the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Returns: + Callable[[~.UpdateTransitionRouteGroupRequest], + ~.TransitionRouteGroup]: + 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_transition_route_group" not in self._stubs: + self._stubs[ + "update_transition_route_group" + ] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.TransitionRouteGroups/UpdateTransitionRouteGroup", + request_serializer=gcdc_transition_route_group.UpdateTransitionRouteGroupRequest.serialize, + response_deserializer=gcdc_transition_route_group.TransitionRouteGroup.deserialize, + ) + return self._stubs["update_transition_route_group"] + + @property + def delete_transition_route_group( + self, + ) -> Callable[ + [transition_route_group.DeleteTransitionRouteGroupRequest], empty.Empty + ]: + r"""Return a callable for the delete transition route group method over gRPC. + + Deletes the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Returns: + Callable[[~.DeleteTransitionRouteGroupRequest], + ~.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_transition_route_group" not in self._stubs: + self._stubs[ + "delete_transition_route_group" + ] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.TransitionRouteGroups/DeleteTransitionRouteGroup", + request_serializer=transition_route_group.DeleteTransitionRouteGroupRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_transition_route_group"] + + +__all__ = ("TransitionRouteGroupsGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/grpc_asyncio.py new file mode 100644 index 00000000..316a971e --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/transition_route_groups/transports/grpc_asyncio.py @@ -0,0 +1,399 @@ +# -*- 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.dialogflowcx_v3.types import transition_route_group +from google.cloud.dialogflowcx_v3.types import ( + transition_route_group as gcdc_transition_route_group, +) +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import TransitionRouteGroupsTransport, DEFAULT_CLIENT_INFO +from .grpc import TransitionRouteGroupsGrpcTransport + + +class TransitionRouteGroupsGrpcAsyncIOTransport(TransitionRouteGroupsTransport): + """gRPC AsyncIO backend transport for TransitionRouteGroups. + + Service for managing + [TransitionRouteGroups][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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_transition_route_groups( + self, + ) -> Callable[ + [transition_route_group.ListTransitionRouteGroupsRequest], + Awaitable[transition_route_group.ListTransitionRouteGroupsResponse], + ]: + r"""Return a callable for the list transition route groups method over gRPC. + + Returns the list of all transition route groups in + the specified flow. + + Returns: + Callable[[~.ListTransitionRouteGroupsRequest], + Awaitable[~.ListTransitionRouteGroupsResponse]]: + 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_transition_route_groups" not in self._stubs: + self._stubs["list_transition_route_groups"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.TransitionRouteGroups/ListTransitionRouteGroups", + request_serializer=transition_route_group.ListTransitionRouteGroupsRequest.serialize, + response_deserializer=transition_route_group.ListTransitionRouteGroupsResponse.deserialize, + ) + return self._stubs["list_transition_route_groups"] + + @property + def get_transition_route_group( + self, + ) -> Callable[ + [transition_route_group.GetTransitionRouteGroupRequest], + Awaitable[transition_route_group.TransitionRouteGroup], + ]: + r"""Return a callable for the get transition route group method over gRPC. + + Retrieves the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Returns: + Callable[[~.GetTransitionRouteGroupRequest], + Awaitable[~.TransitionRouteGroup]]: + 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_transition_route_group" not in self._stubs: + self._stubs["get_transition_route_group"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.TransitionRouteGroups/GetTransitionRouteGroup", + request_serializer=transition_route_group.GetTransitionRouteGroupRequest.serialize, + response_deserializer=transition_route_group.TransitionRouteGroup.deserialize, + ) + return self._stubs["get_transition_route_group"] + + @property + def create_transition_route_group( + self, + ) -> Callable[ + [gcdc_transition_route_group.CreateTransitionRouteGroupRequest], + Awaitable[gcdc_transition_route_group.TransitionRouteGroup], + ]: + r"""Return a callable for the create transition route group method over gRPC. + + Creates an + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup] + in the specified flow. + + Returns: + Callable[[~.CreateTransitionRouteGroupRequest], + Awaitable[~.TransitionRouteGroup]]: + 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_transition_route_group" not in self._stubs: + self._stubs[ + "create_transition_route_group" + ] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.TransitionRouteGroups/CreateTransitionRouteGroup", + request_serializer=gcdc_transition_route_group.CreateTransitionRouteGroupRequest.serialize, + response_deserializer=gcdc_transition_route_group.TransitionRouteGroup.deserialize, + ) + return self._stubs["create_transition_route_group"] + + @property + def update_transition_route_group( + self, + ) -> Callable[ + [gcdc_transition_route_group.UpdateTransitionRouteGroupRequest], + Awaitable[gcdc_transition_route_group.TransitionRouteGroup], + ]: + r"""Return a callable for the update transition route group method over gRPC. + + Updates the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Returns: + Callable[[~.UpdateTransitionRouteGroupRequest], + Awaitable[~.TransitionRouteGroup]]: + 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_transition_route_group" not in self._stubs: + self._stubs[ + "update_transition_route_group" + ] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.TransitionRouteGroups/UpdateTransitionRouteGroup", + request_serializer=gcdc_transition_route_group.UpdateTransitionRouteGroupRequest.serialize, + response_deserializer=gcdc_transition_route_group.TransitionRouteGroup.deserialize, + ) + return self._stubs["update_transition_route_group"] + + @property + def delete_transition_route_group( + self, + ) -> Callable[ + [transition_route_group.DeleteTransitionRouteGroupRequest], + Awaitable[empty.Empty], + ]: + r"""Return a callable for the delete transition route group method over gRPC. + + Deletes the specified + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + + Returns: + Callable[[~.DeleteTransitionRouteGroupRequest], + 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_transition_route_group" not in self._stubs: + self._stubs[ + "delete_transition_route_group" + ] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.TransitionRouteGroups/DeleteTransitionRouteGroup", + request_serializer=transition_route_group.DeleteTransitionRouteGroupRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_transition_route_group"] + + +__all__ = ("TransitionRouteGroupsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/versions/__init__.py b/google/cloud/dialogflowcx_v3/services/versions/__init__.py new file mode 100644 index 00000000..93418cef --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/versions/__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 VersionsClient +from .async_client import VersionsAsyncClient + +__all__ = ( + "VersionsClient", + "VersionsAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/versions/async_client.py b/google/cloud/dialogflowcx_v3/services/versions/async_client.py new file mode 100644 index 00000000..410e775f --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/versions/async_client.py @@ -0,0 +1,654 @@ +# -*- 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.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.dialogflowcx_v3.services.versions import pagers +from google.cloud.dialogflowcx_v3.types import flow +from google.cloud.dialogflowcx_v3.types import version +from google.cloud.dialogflowcx_v3.types import version as gcdc_version +from google.protobuf import empty_pb2 as empty # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import VersionsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import VersionsGrpcAsyncIOTransport +from .client import VersionsClient + + +class VersionsAsyncClient: + """Service for managing + [Versions][google.cloud.dialogflow.cx.v3.Version]. + """ + + _client: VersionsClient + + DEFAULT_ENDPOINT = VersionsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = VersionsClient.DEFAULT_MTLS_ENDPOINT + + version_path = staticmethod(VersionsClient.version_path) + parse_version_path = staticmethod(VersionsClient.parse_version_path) + + common_billing_account_path = staticmethod( + VersionsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + VersionsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(VersionsClient.common_folder_path) + parse_common_folder_path = staticmethod(VersionsClient.parse_common_folder_path) + + common_organization_path = staticmethod(VersionsClient.common_organization_path) + parse_common_organization_path = staticmethod( + VersionsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(VersionsClient.common_project_path) + parse_common_project_path = staticmethod(VersionsClient.parse_common_project_path) + + common_location_path = staticmethod(VersionsClient.common_location_path) + parse_common_location_path = staticmethod(VersionsClient.parse_common_location_path) + + from_service_account_file = VersionsClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> VersionsTransport: + """Return the transport used by the client instance. + + Returns: + VersionsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(VersionsClient).get_transport_class, type(VersionsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, VersionsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the versions 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, ~.VersionsTransport]): 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 = VersionsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_versions( + self, + request: version.ListVersionsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListVersionsAsyncPager: + r"""Returns the list of all versions in the specified + [Flow][google.cloud.dialogflow.cx.v3.Flow]. + + Args: + request (:class:`~.version.ListVersionsRequest`): + The request object. The request message for + [Versions.ListVersions][google.cloud.dialogflow.cx.v3.Versions.ListVersions]. + parent (:class:`str`): + Required. The [Flow][google.cloud.dialogflow.cx.v3.Flow] + to list all versions for. Format: + ``projects//locations//agents//flows/``. + 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: + ~.pagers.ListVersionsAsyncPager: + The response message for + [Versions.ListVersions][google.cloud.dialogflow.cx.v3.Versions.ListVersions]. + + 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 = version.ListVersionsRequest(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_versions, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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.ListVersionsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_version( + self, + request: version.GetVersionRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> version.Version: + r"""Retrieves the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Args: + request (:class:`~.version.GetVersionRequest`): + The request object. The request message for + [Versions.GetVersion][google.cloud.dialogflow.cx.v3.Versions.GetVersion]. + name (:class:`str`): + Required. The name of the + [Version][google.cloud.dialogflow.cx.v3.Version]. + Format: + ``projects//locations//agents//flows//versions/``. + 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: + ~.version.Version: + Represents a version of a flow. + """ + # 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 = version.GetVersionRequest(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_version, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_version( + self, + request: gcdc_version.CreateVersionRequest = None, + *, + parent: str = None, + version: gcdc_version.Version = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Creates a [Version][google.cloud.dialogflow.cx.v3.Version] in + the specified [Flow][google.cloud.dialogflow.cx.v3.Flow]. + + Args: + request (:class:`~.gcdc_version.CreateVersionRequest`): + The request object. The request message for + [Versions.CreateVersion][google.cloud.dialogflow.cx.v3.Versions.CreateVersion]. + parent (:class:`str`): + Required. The [Flow][google.cloud.dialogflow.cx.v3.Flow] + to create an + [Version][google.cloud.dialogflow.cx.v3.Version] for. + Format: + ``projects//locations//agents//flows/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + version (:class:`~.gcdc_version.Version`): + Required. The version to create. + This corresponds to the ``version`` 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: + ~.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.gcdc_version.Version``: Represents a version + of a flow. + + """ + # 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, version]) + 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 = gcdc_version.CreateVersionRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if version is not None: + request.version = version + + # 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_version, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", request.parent),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + gcdc_version.Version, + metadata_type=gcdc_version.CreateVersionOperationMetadata, + ) + + # Done; return the response. + return response + + async def update_version( + self, + request: gcdc_version.UpdateVersionRequest = None, + *, + version: gcdc_version.Version = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_version.Version: + r"""Updates the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Args: + request (:class:`~.gcdc_version.UpdateVersionRequest`): + The request object. The request message for + [Versions.UpdateVersion][google.cloud.dialogflow.cx.v3.Versions.UpdateVersion]. + version (:class:`~.gcdc_version.Version`): + Required. The version to update. + This corresponds to the ``version`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + Required. The mask to control which fields get updated. + Currently only ``description`` and ``display_name`` can + be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_version.Version: + Represents a version of a flow. + """ + # 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([version, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = gcdc_version.UpdateVersionRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if version is not None: + request.version = version + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_version, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("version.name", request.version.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_version( + self, + request: version.DeleteVersionRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Args: + request (:class:`~.version.DeleteVersionRequest`): + The request object. The request message for + [Versions.DeleteVersion][google.cloud.dialogflow.cx.v3.Versions.DeleteVersion]. + name (:class:`str`): + Required. The name of the + [Version][google.cloud.dialogflow.cx.v3.Version] to + delete. Format: + ``projects//locations//agents//flows//versions/``. + 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 = version.DeleteVersionRequest(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_version, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + await rpc( + request, retry=retry, timeout=timeout, metadata=metadata, + ) + + async def load_version( + self, + request: version.LoadVersionRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Loads a specified version to draft version. + + Args: + request (:class:`~.version.LoadVersionRequest`): + The request object. The request message for + [Versions.LoadVersion][google.cloud.dialogflow.cx.v3.Versions.LoadVersion]. + name (:class:`str`): + Required. The + [Version][google.cloud.dialogflow.cx.v3.Version] to be + loaded to draft version. Format: + ``projects//locations//agents//flows//versions/``. + 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: + ~.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.empty.Empty``: A generic empty message that + you can re-use to avoid defining duplicated empty + messages in your APIs. A typical example is to use it as + the request or the response type of an API method. For + instance: + + :: + + service Foo { + rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); + } + + The JSON representation for ``Empty`` is empty JSON + object ``{}``. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = version.LoadVersionRequest(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.load_version, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + empty.Empty, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("VersionsAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/versions/client.py b/google/cloud/dialogflowcx_v3/services/versions/client.py new file mode 100644 index 00000000..2cf5e748 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/versions/client.py @@ -0,0 +1,859 @@ +# -*- 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.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore +from google.cloud.dialogflowcx_v3.services.versions import pagers +from google.cloud.dialogflowcx_v3.types import flow +from google.cloud.dialogflowcx_v3.types import version +from google.cloud.dialogflowcx_v3.types import version as gcdc_version +from google.protobuf import empty_pb2 as empty # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import VersionsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import VersionsGrpcTransport +from .transports.grpc_asyncio import VersionsGrpcAsyncIOTransport + + +class VersionsClientMeta(type): + """Metaclass for the Versions 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[VersionsTransport]] + _transport_registry["grpc"] = VersionsGrpcTransport + _transport_registry["grpc_asyncio"] = VersionsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[VersionsTransport]: + """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 VersionsClient(metaclass=VersionsClientMeta): + """Service for managing + [Versions][google.cloud.dialogflow.cx.v3.Version]. + """ + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> VersionsTransport: + """Return the transport used by the client instance. + + Returns: + VersionsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def version_path( + project: str, location: str, agent: str, flow: str, version: str, + ) -> str: + """Return a fully-qualified version string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/versions/{version}".format( + project=project, location=location, agent=agent, flow=flow, version=version, + ) + + @staticmethod + def parse_version_path(path: str) -> Dict[str, str]: + """Parse a version path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/versions/(?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, VersionsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the versions 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, ~.VersionsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, VersionsTransport): + # transport is a VersionsTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_versions( + self, + request: version.ListVersionsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListVersionsPager: + r"""Returns the list of all versions in the specified + [Flow][google.cloud.dialogflow.cx.v3.Flow]. + + Args: + request (:class:`~.version.ListVersionsRequest`): + The request object. The request message for + [Versions.ListVersions][google.cloud.dialogflow.cx.v3.Versions.ListVersions]. + parent (:class:`str`): + Required. The [Flow][google.cloud.dialogflow.cx.v3.Flow] + to list all versions for. Format: + ``projects//locations//agents//flows/``. + 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: + ~.pagers.ListVersionsPager: + The response message for + [Versions.ListVersions][google.cloud.dialogflow.cx.v3.Versions.ListVersions]. + + 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 version.ListVersionsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, version.ListVersionsRequest): + request = version.ListVersionsRequest(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_versions] + + # 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.ListVersionsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_version( + self, + request: version.GetVersionRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> version.Version: + r"""Retrieves the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Args: + request (:class:`~.version.GetVersionRequest`): + The request object. The request message for + [Versions.GetVersion][google.cloud.dialogflow.cx.v3.Versions.GetVersion]. + name (:class:`str`): + Required. The name of the + [Version][google.cloud.dialogflow.cx.v3.Version]. + Format: + ``projects//locations//agents//flows//versions/``. + 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: + ~.version.Version: + Represents a version of a flow. + """ + # 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 version.GetVersionRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, version.GetVersionRequest): + request = version.GetVersionRequest(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_version] + + # 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_version( + self, + request: gcdc_version.CreateVersionRequest = None, + *, + parent: str = None, + version: gcdc_version.Version = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Creates a [Version][google.cloud.dialogflow.cx.v3.Version] in + the specified [Flow][google.cloud.dialogflow.cx.v3.Flow]. + + Args: + request (:class:`~.gcdc_version.CreateVersionRequest`): + The request object. The request message for + [Versions.CreateVersion][google.cloud.dialogflow.cx.v3.Versions.CreateVersion]. + parent (:class:`str`): + Required. The [Flow][google.cloud.dialogflow.cx.v3.Flow] + to create an + [Version][google.cloud.dialogflow.cx.v3.Version] for. + Format: + ``projects//locations//agents//flows/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + version (:class:`~.gcdc_version.Version`): + Required. The version to create. + This corresponds to the ``version`` 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: + ~.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.gcdc_version.Version``: Represents a version + of a flow. + + """ + # 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, version]) + 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 gcdc_version.CreateVersionRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_version.CreateVersionRequest): + request = gcdc_version.CreateVersionRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if version is not None: + request.version = version + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_version] + + # 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,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + gcdc_version.Version, + metadata_type=gcdc_version.CreateVersionOperationMetadata, + ) + + # Done; return the response. + return response + + def update_version( + self, + request: gcdc_version.UpdateVersionRequest = None, + *, + version: gcdc_version.Version = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_version.Version: + r"""Updates the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Args: + request (:class:`~.gcdc_version.UpdateVersionRequest`): + The request object. The request message for + [Versions.UpdateVersion][google.cloud.dialogflow.cx.v3.Versions.UpdateVersion]. + version (:class:`~.gcdc_version.Version`): + Required. The version to update. + This corresponds to the ``version`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + Required. The mask to control which fields get updated. + Currently only ``description`` and ``display_name`` can + be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_version.Version: + Represents a version of a flow. + """ + # 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([version, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a gcdc_version.UpdateVersionRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_version.UpdateVersionRequest): + request = gcdc_version.UpdateVersionRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if version is not None: + request.version = version + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_version] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("version.name", request.version.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def delete_version( + self, + request: version.DeleteVersionRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Args: + request (:class:`~.version.DeleteVersionRequest`): + The request object. The request message for + [Versions.DeleteVersion][google.cloud.dialogflow.cx.v3.Versions.DeleteVersion]. + name (:class:`str`): + Required. The name of the + [Version][google.cloud.dialogflow.cx.v3.Version] to + delete. Format: + ``projects//locations//agents//flows//versions/``. + 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 version.DeleteVersionRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, version.DeleteVersionRequest): + request = version.DeleteVersionRequest(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_version] + + # 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, + ) + + def load_version( + self, + request: version.LoadVersionRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Loads a specified version to draft version. + + Args: + request (:class:`~.version.LoadVersionRequest`): + The request object. The request message for + [Versions.LoadVersion][google.cloud.dialogflow.cx.v3.Versions.LoadVersion]. + name (:class:`str`): + Required. The + [Version][google.cloud.dialogflow.cx.v3.Version] to be + loaded to draft version. Format: + ``projects//locations//agents//flows//versions/``. + 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: + ~.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:``~.empty.Empty``: A generic empty message that + you can re-use to avoid defining duplicated empty + messages in your APIs. A typical example is to use it as + the request or the response type of an API method. For + instance: + + :: + + service Foo { + rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); + } + + The JSON representation for ``Empty`` is empty JSON + object ``{}``. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a version.LoadVersionRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, version.LoadVersionRequest): + request = version.LoadVersionRequest(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.load_version] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + empty.Empty, + metadata_type=struct.Struct, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("VersionsClient",) diff --git a/google/cloud/dialogflowcx_v3/services/versions/pagers.py b/google/cloud/dialogflowcx_v3/services/versions/pagers.py new file mode 100644 index 00000000..decd7e0e --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/versions/pagers.py @@ -0,0 +1,148 @@ +# -*- 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 + +from google.cloud.dialogflowcx_v3.types import version + + +class ListVersionsPager: + """A pager for iterating through ``list_versions`` requests. + + This class thinly wraps an initial + :class:`~.version.ListVersionsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``versions`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListVersions`` requests and continue to iterate + through the ``versions`` field on the + corresponding responses. + + All the usual :class:`~.version.ListVersionsResponse` + 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[..., version.ListVersionsResponse], + request: version.ListVersionsRequest, + response: version.ListVersionsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.version.ListVersionsRequest`): + The initial request object. + response (:class:`~.version.ListVersionsResponse`): + 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 = version.ListVersionsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[version.ListVersionsResponse]: + 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[version.Version]: + for page in self.pages: + yield from page.versions + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListVersionsAsyncPager: + """A pager for iterating through ``list_versions`` requests. + + This class thinly wraps an initial + :class:`~.version.ListVersionsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``versions`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListVersions`` requests and continue to iterate + through the ``versions`` field on the + corresponding responses. + + All the usual :class:`~.version.ListVersionsResponse` + 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[version.ListVersionsResponse]], + request: version.ListVersionsRequest, + response: version.ListVersionsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.version.ListVersionsRequest`): + The initial request object. + response (:class:`~.version.ListVersionsResponse`): + 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 = version.ListVersionsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[version.ListVersionsResponse]: + 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[version.Version]: + async def async_generator(): + async for page in self.pages: + for response in page.versions: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflowcx_v3/services/versions/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/versions/transports/__init__.py new file mode 100644 index 00000000..7801b147 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/versions/transports/__init__.py @@ -0,0 +1,36 @@ +# -*- 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 VersionsTransport +from .grpc import VersionsGrpcTransport +from .grpc_asyncio import VersionsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[VersionsTransport]] +_transport_registry["grpc"] = VersionsGrpcTransport +_transport_registry["grpc_asyncio"] = VersionsGrpcAsyncIOTransport + + +__all__ = ( + "VersionsTransport", + "VersionsGrpcTransport", + "VersionsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/versions/transports/base.py b/google/cloud/dialogflowcx_v3/services/versions/transports/base.py new file mode 100644 index 00000000..3b08f675 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/versions/transports/base.py @@ -0,0 +1,198 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +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.api_core import operations_v1 # type: ignore +from google.auth import credentials # type: ignore + +from google.cloud.dialogflowcx_v3.types import version +from google.cloud.dialogflowcx_v3.types import version as gcdc_version +from google.longrunning import operations_pb2 as operations # type: ignore +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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class VersionsTransport(abc.ABC): + """Abstract transport class for Versions.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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_versions: gapic_v1.method.wrap_method( + self.list_versions, default_timeout=None, client_info=client_info, + ), + self.get_version: gapic_v1.method.wrap_method( + self.get_version, default_timeout=None, client_info=client_info, + ), + self.create_version: gapic_v1.method.wrap_method( + self.create_version, default_timeout=None, client_info=client_info, + ), + self.update_version: gapic_v1.method.wrap_method( + self.update_version, default_timeout=None, client_info=client_info, + ), + self.delete_version: gapic_v1.method.wrap_method( + self.delete_version, default_timeout=None, client_info=client_info, + ), + self.load_version: gapic_v1.method.wrap_method( + self.load_version, default_timeout=None, client_info=client_info, + ), + } + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Return the client designed to process long-running operations.""" + raise NotImplementedError() + + @property + def list_versions( + self, + ) -> typing.Callable[ + [version.ListVersionsRequest], + typing.Union[ + version.ListVersionsResponse, typing.Awaitable[version.ListVersionsResponse] + ], + ]: + raise NotImplementedError() + + @property + def get_version( + self, + ) -> typing.Callable[ + [version.GetVersionRequest], + typing.Union[version.Version, typing.Awaitable[version.Version]], + ]: + raise NotImplementedError() + + @property + def create_version( + self, + ) -> typing.Callable[ + [gcdc_version.CreateVersionRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + + @property + def update_version( + self, + ) -> typing.Callable[ + [gcdc_version.UpdateVersionRequest], + typing.Union[gcdc_version.Version, typing.Awaitable[gcdc_version.Version]], + ]: + raise NotImplementedError() + + @property + def delete_version( + self, + ) -> typing.Callable[ + [version.DeleteVersionRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + @property + def load_version( + self, + ) -> typing.Callable[ + [version.LoadVersionRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + + +__all__ = ("VersionsTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/versions/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/versions/transports/grpc.py new file mode 100644 index 00000000..2bf9dfaf --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/versions/transports/grpc.py @@ -0,0 +1,411 @@ +# -*- 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 operations_v1 # 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.dialogflowcx_v3.types import version +from google.cloud.dialogflowcx_v3.types import version as gcdc_version +from google.longrunning import operations_pb2 as operations # type: ignore +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import VersionsTransport, DEFAULT_CLIENT_INFO + + +class VersionsGrpcTransport(VersionsTransport): + """gRPC backend transport for Versions. + + Service for managing + [Versions][google.cloud.dialogflow.cx.v3.Version]. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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 operations_client(self) -> operations_v1.OperationsClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if "operations_client" not in self.__dict__: + self.__dict__["operations_client"] = operations_v1.OperationsClient( + self.grpc_channel + ) + + # Return the client from cache. + return self.__dict__["operations_client"] + + @property + def list_versions( + self, + ) -> Callable[[version.ListVersionsRequest], version.ListVersionsResponse]: + r"""Return a callable for the list versions method over gRPC. + + Returns the list of all versions in the specified + [Flow][google.cloud.dialogflow.cx.v3.Flow]. + + Returns: + Callable[[~.ListVersionsRequest], + ~.ListVersionsResponse]: + 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_versions" not in self._stubs: + self._stubs["list_versions"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/ListVersions", + request_serializer=version.ListVersionsRequest.serialize, + response_deserializer=version.ListVersionsResponse.deserialize, + ) + return self._stubs["list_versions"] + + @property + def get_version(self) -> Callable[[version.GetVersionRequest], version.Version]: + r"""Return a callable for the get version method over gRPC. + + Retrieves the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Returns: + Callable[[~.GetVersionRequest], + ~.Version]: + 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_version" not in self._stubs: + self._stubs["get_version"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/GetVersion", + request_serializer=version.GetVersionRequest.serialize, + response_deserializer=version.Version.deserialize, + ) + return self._stubs["get_version"] + + @property + def create_version( + self, + ) -> Callable[[gcdc_version.CreateVersionRequest], operations.Operation]: + r"""Return a callable for the create version method over gRPC. + + Creates a [Version][google.cloud.dialogflow.cx.v3.Version] in + the specified [Flow][google.cloud.dialogflow.cx.v3.Flow]. + + Returns: + Callable[[~.CreateVersionRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_version" not in self._stubs: + self._stubs["create_version"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/CreateVersion", + request_serializer=gcdc_version.CreateVersionRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["create_version"] + + @property + def update_version( + self, + ) -> Callable[[gcdc_version.UpdateVersionRequest], gcdc_version.Version]: + r"""Return a callable for the update version method over gRPC. + + Updates the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Returns: + Callable[[~.UpdateVersionRequest], + ~.Version]: + 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_version" not in self._stubs: + self._stubs["update_version"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/UpdateVersion", + request_serializer=gcdc_version.UpdateVersionRequest.serialize, + response_deserializer=gcdc_version.Version.deserialize, + ) + return self._stubs["update_version"] + + @property + def delete_version(self) -> Callable[[version.DeleteVersionRequest], empty.Empty]: + r"""Return a callable for the delete version method over gRPC. + + Deletes the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Returns: + Callable[[~.DeleteVersionRequest], + ~.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_version" not in self._stubs: + self._stubs["delete_version"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/DeleteVersion", + request_serializer=version.DeleteVersionRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_version"] + + @property + def load_version( + self, + ) -> Callable[[version.LoadVersionRequest], operations.Operation]: + r"""Return a callable for the load version method over gRPC. + + Loads a specified version to draft version. + + Returns: + Callable[[~.LoadVersionRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "load_version" not in self._stubs: + self._stubs["load_version"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/LoadVersion", + request_serializer=version.LoadVersionRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["load_version"] + + +__all__ = ("VersionsGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/versions/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/versions/transports/grpc_asyncio.py new file mode 100644 index 00000000..4cef08ce --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/versions/transports/grpc_asyncio.py @@ -0,0 +1,421 @@ +# -*- 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.api_core import operations_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 grpc.experimental import aio # type: ignore + +from google.cloud.dialogflowcx_v3.types import version +from google.cloud.dialogflowcx_v3.types import version as gcdc_version +from google.longrunning import operations_pb2 as operations # type: ignore +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import VersionsTransport, DEFAULT_CLIENT_INFO +from .grpc import VersionsGrpcTransport + + +class VersionsGrpcAsyncIOTransport(VersionsTransport): + """gRPC AsyncIO backend transport for Versions. + + Service for managing + [Versions][google.cloud.dialogflow.cx.v3.Version]. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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 operations_client(self) -> operations_v1.OperationsAsyncClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if "operations_client" not in self.__dict__: + self.__dict__["operations_client"] = operations_v1.OperationsAsyncClient( + self.grpc_channel + ) + + # Return the client from cache. + return self.__dict__["operations_client"] + + @property + def list_versions( + self, + ) -> Callable[ + [version.ListVersionsRequest], Awaitable[version.ListVersionsResponse] + ]: + r"""Return a callable for the list versions method over gRPC. + + Returns the list of all versions in the specified + [Flow][google.cloud.dialogflow.cx.v3.Flow]. + + Returns: + Callable[[~.ListVersionsRequest], + Awaitable[~.ListVersionsResponse]]: + 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_versions" not in self._stubs: + self._stubs["list_versions"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/ListVersions", + request_serializer=version.ListVersionsRequest.serialize, + response_deserializer=version.ListVersionsResponse.deserialize, + ) + return self._stubs["list_versions"] + + @property + def get_version( + self, + ) -> Callable[[version.GetVersionRequest], Awaitable[version.Version]]: + r"""Return a callable for the get version method over gRPC. + + Retrieves the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Returns: + Callable[[~.GetVersionRequest], + Awaitable[~.Version]]: + 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_version" not in self._stubs: + self._stubs["get_version"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/GetVersion", + request_serializer=version.GetVersionRequest.serialize, + response_deserializer=version.Version.deserialize, + ) + return self._stubs["get_version"] + + @property + def create_version( + self, + ) -> Callable[[gcdc_version.CreateVersionRequest], Awaitable[operations.Operation]]: + r"""Return a callable for the create version method over gRPC. + + Creates a [Version][google.cloud.dialogflow.cx.v3.Version] in + the specified [Flow][google.cloud.dialogflow.cx.v3.Flow]. + + Returns: + Callable[[~.CreateVersionRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "create_version" not in self._stubs: + self._stubs["create_version"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/CreateVersion", + request_serializer=gcdc_version.CreateVersionRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["create_version"] + + @property + def update_version( + self, + ) -> Callable[[gcdc_version.UpdateVersionRequest], Awaitable[gcdc_version.Version]]: + r"""Return a callable for the update version method over gRPC. + + Updates the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Returns: + Callable[[~.UpdateVersionRequest], + Awaitable[~.Version]]: + 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_version" not in self._stubs: + self._stubs["update_version"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/UpdateVersion", + request_serializer=gcdc_version.UpdateVersionRequest.serialize, + response_deserializer=gcdc_version.Version.deserialize, + ) + return self._stubs["update_version"] + + @property + def delete_version( + self, + ) -> Callable[[version.DeleteVersionRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete version method over gRPC. + + Deletes the specified + [Version][google.cloud.dialogflow.cx.v3.Version]. + + Returns: + Callable[[~.DeleteVersionRequest], + 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_version" not in self._stubs: + self._stubs["delete_version"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/DeleteVersion", + request_serializer=version.DeleteVersionRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_version"] + + @property + def load_version( + self, + ) -> Callable[[version.LoadVersionRequest], Awaitable[operations.Operation]]: + r"""Return a callable for the load version method over gRPC. + + Loads a specified version to draft version. + + Returns: + Callable[[~.LoadVersionRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "load_version" not in self._stubs: + self._stubs["load_version"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Versions/LoadVersion", + request_serializer=version.LoadVersionRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["load_version"] + + +__all__ = ("VersionsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/webhooks/__init__.py b/google/cloud/dialogflowcx_v3/services/webhooks/__init__.py new file mode 100644 index 00000000..2483e3be --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/webhooks/__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 WebhooksClient +from .async_client import WebhooksAsyncClient + +__all__ = ( + "WebhooksClient", + "WebhooksAsyncClient", +) diff --git a/google/cloud/dialogflowcx_v3/services/webhooks/async_client.py b/google/cloud/dialogflowcx_v3/services/webhooks/async_client.py new file mode 100644 index 00000000..c7392227 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/webhooks/async_client.py @@ -0,0 +1,548 @@ +# -*- 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.dialogflowcx_v3.services.webhooks import pagers +from google.cloud.dialogflowcx_v3.types import webhook +from google.cloud.dialogflowcx_v3.types import webhook as gcdc_webhook +from google.protobuf import duration_pb2 as duration # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import WebhooksTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import WebhooksGrpcAsyncIOTransport +from .client import WebhooksClient + + +class WebhooksAsyncClient: + """Service for managing + [Webhooks][google.cloud.dialogflow.cx.v3.Webhook]. + """ + + _client: WebhooksClient + + DEFAULT_ENDPOINT = WebhooksClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = WebhooksClient.DEFAULT_MTLS_ENDPOINT + + webhook_path = staticmethod(WebhooksClient.webhook_path) + parse_webhook_path = staticmethod(WebhooksClient.parse_webhook_path) + + common_billing_account_path = staticmethod( + WebhooksClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + WebhooksClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(WebhooksClient.common_folder_path) + parse_common_folder_path = staticmethod(WebhooksClient.parse_common_folder_path) + + common_organization_path = staticmethod(WebhooksClient.common_organization_path) + parse_common_organization_path = staticmethod( + WebhooksClient.parse_common_organization_path + ) + + common_project_path = staticmethod(WebhooksClient.common_project_path) + parse_common_project_path = staticmethod(WebhooksClient.parse_common_project_path) + + common_location_path = staticmethod(WebhooksClient.common_location_path) + parse_common_location_path = staticmethod(WebhooksClient.parse_common_location_path) + + from_service_account_file = WebhooksClient.from_service_account_file + from_service_account_json = from_service_account_file + + @property + def transport(self) -> WebhooksTransport: + """Return the transport used by the client instance. + + Returns: + WebhooksTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(WebhooksClient).get_transport_class, type(WebhooksClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, WebhooksTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the webhooks 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, ~.WebhooksTransport]): 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 = WebhooksClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_webhooks( + self, + request: webhook.ListWebhooksRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListWebhooksAsyncPager: + r"""Returns the list of all webhooks in the specified + agent. + + Args: + request (:class:`~.webhook.ListWebhooksRequest`): + The request object. The request message for + [Webhooks.ListWebhooks][google.cloud.dialogflow.cx.v3.Webhooks.ListWebhooks]. + parent (:class:`str`): + Required. The agent to list all webhooks for. Format: + ``projects//locations//agents/``. + 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: + ~.pagers.ListWebhooksAsyncPager: + The response message for + [Webhooks.ListWebhooks][google.cloud.dialogflow.cx.v3.Webhooks.ListWebhooks]. + + 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 = webhook.ListWebhooksRequest(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_webhooks, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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.ListWebhooksAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_webhook( + self, + request: webhook.GetWebhookRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> webhook.Webhook: + r"""Retrieves the specified webhook. + + Args: + request (:class:`~.webhook.GetWebhookRequest`): + The request object. The request message for + [Webhooks.GetWebhook][google.cloud.dialogflow.cx.v3.Webhooks.GetWebhook]. + name (:class:`str`): + Required. The name of the webhook. Format: + ``projects//locations//agents//webhooks/``. + 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: + ~.webhook.Webhook: + Webhooks host the developer's + business logic. During a session, + webhooks allow the developer to use the + data extracted by Dialogflow's natural + language processing to generate dynamic + responses, validate collected data, or + trigger actions on the backend. + + """ + # 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 = webhook.GetWebhookRequest(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_webhook, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_webhook( + self, + request: gcdc_webhook.CreateWebhookRequest = None, + *, + parent: str = None, + webhook: gcdc_webhook.Webhook = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_webhook.Webhook: + r"""Creates a webhook in the specified agent. + + Args: + request (:class:`~.gcdc_webhook.CreateWebhookRequest`): + The request object. The request message for + [Webhooks.CreateWebhook][google.cloud.dialogflow.cx.v3.Webhooks.CreateWebhook]. + parent (:class:`str`): + Required. The agent to create a webhook for. Format: + ``projects//locations//agents/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + webhook (:class:`~.gcdc_webhook.Webhook`): + Required. The webhook to create. + This corresponds to the ``webhook`` 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: + ~.gcdc_webhook.Webhook: + Webhooks host the developer's + business logic. During a session, + webhooks allow the developer to use the + data extracted by Dialogflow's natural + language processing to generate dynamic + responses, validate collected data, or + trigger actions on the backend. + + """ + # 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, webhook]) + 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 = gcdc_webhook.CreateWebhookRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if webhook is not None: + request.webhook = webhook + + # 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_webhook, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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_webhook( + self, + request: gcdc_webhook.UpdateWebhookRequest = None, + *, + webhook: gcdc_webhook.Webhook = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_webhook.Webhook: + r"""Updates the specified webhook. + + Args: + request (:class:`~.gcdc_webhook.UpdateWebhookRequest`): + The request object. The request message for + [Webhooks.UpdateWebhook][google.cloud.dialogflow.cx.v3.Webhooks.UpdateWebhook]. + webhook (:class:`~.gcdc_webhook.Webhook`): + Required. The webhook to update. + This corresponds to the ``webhook`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. If the mask is not present, all + fields will be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_webhook.Webhook: + Webhooks host the developer's + business logic. During a session, + webhooks allow the developer to use the + data extracted by Dialogflow's natural + language processing to generate dynamic + responses, validate collected data, or + trigger actions on the backend. + + """ + # 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([webhook, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = gcdc_webhook.UpdateWebhookRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if webhook is not None: + request.webhook = webhook + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.update_webhook, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("webhook.name", request.webhook.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_webhook( + self, + request: webhook.DeleteWebhookRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified webhook. + + Args: + request (:class:`~.webhook.DeleteWebhookRequest`): + The request object. The request message for + [Webhooks.DeleteWebhook][google.cloud.dialogflow.cx.v3.Webhooks.DeleteWebhook]. + name (:class:`str`): + Required. The name of the webhook to delete. Format: + ``projects//locations//agents//webhooks/``. + 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 = webhook.DeleteWebhookRequest(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_webhook, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("WebhooksAsyncClient",) diff --git a/google/cloud/dialogflowcx_v3/services/webhooks/client.py b/google/cloud/dialogflowcx_v3/services/webhooks/client.py new file mode 100644 index 00000000..8736fa0e --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/webhooks/client.py @@ -0,0 +1,750 @@ +# -*- 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.dialogflowcx_v3.services.webhooks import pagers +from google.cloud.dialogflowcx_v3.types import webhook +from google.cloud.dialogflowcx_v3.types import webhook as gcdc_webhook +from google.protobuf import duration_pb2 as duration # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import WebhooksTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import WebhooksGrpcTransport +from .transports.grpc_asyncio import WebhooksGrpcAsyncIOTransport + + +class WebhooksClientMeta(type): + """Metaclass for the Webhooks 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[WebhooksTransport]] + _transport_registry["grpc"] = WebhooksGrpcTransport + _transport_registry["grpc_asyncio"] = WebhooksGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[WebhooksTransport]: + """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 WebhooksClient(metaclass=WebhooksClientMeta): + """Service for managing + [Webhooks][google.cloud.dialogflow.cx.v3.Webhook]. + """ + + @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 = "dialogflow.googleapis.com" + DEFAULT_MTLS_ENDPOINT = _get_default_mtls_endpoint.__func__( # type: ignore + DEFAULT_ENDPOINT + ) + + @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: + {@api.name}: 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) -> WebhooksTransport: + """Return the transport used by the client instance. + + Returns: + WebhooksTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def webhook_path(project: str, location: str, agent: str, webhook: str,) -> str: + """Return a fully-qualified webhook string.""" + return "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + + @staticmethod + def parse_webhook_path(path: str) -> Dict[str, str]: + """Parse a webhook path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/webhooks/(?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, WebhooksTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the webhooks 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, ~.WebhooksTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (client_options_lib.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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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, WebhooksTransport): + # transport is a WebhooksTransport 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, + ssl_channel_credentials=ssl_credentials, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_webhooks( + self, + request: webhook.ListWebhooksRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListWebhooksPager: + r"""Returns the list of all webhooks in the specified + agent. + + Args: + request (:class:`~.webhook.ListWebhooksRequest`): + The request object. The request message for + [Webhooks.ListWebhooks][google.cloud.dialogflow.cx.v3.Webhooks.ListWebhooks]. + parent (:class:`str`): + Required. The agent to list all webhooks for. Format: + ``projects//locations//agents/``. + 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: + ~.pagers.ListWebhooksPager: + The response message for + [Webhooks.ListWebhooks][google.cloud.dialogflow.cx.v3.Webhooks.ListWebhooks]. + + 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 webhook.ListWebhooksRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, webhook.ListWebhooksRequest): + request = webhook.ListWebhooksRequest(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_webhooks] + + # 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.ListWebhooksPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_webhook( + self, + request: webhook.GetWebhookRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> webhook.Webhook: + r"""Retrieves the specified webhook. + + Args: + request (:class:`~.webhook.GetWebhookRequest`): + The request object. The request message for + [Webhooks.GetWebhook][google.cloud.dialogflow.cx.v3.Webhooks.GetWebhook]. + name (:class:`str`): + Required. The name of the webhook. Format: + ``projects//locations//agents//webhooks/``. + 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: + ~.webhook.Webhook: + Webhooks host the developer's + business logic. During a session, + webhooks allow the developer to use the + data extracted by Dialogflow's natural + language processing to generate dynamic + responses, validate collected data, or + trigger actions on the backend. + + """ + # 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 webhook.GetWebhookRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, webhook.GetWebhookRequest): + request = webhook.GetWebhookRequest(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_webhook] + + # 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_webhook( + self, + request: gcdc_webhook.CreateWebhookRequest = None, + *, + parent: str = None, + webhook: gcdc_webhook.Webhook = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_webhook.Webhook: + r"""Creates a webhook in the specified agent. + + Args: + request (:class:`~.gcdc_webhook.CreateWebhookRequest`): + The request object. The request message for + [Webhooks.CreateWebhook][google.cloud.dialogflow.cx.v3.Webhooks.CreateWebhook]. + parent (:class:`str`): + Required. The agent to create a webhook for. Format: + ``projects//locations//agents/``. + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + webhook (:class:`~.gcdc_webhook.Webhook`): + Required. The webhook to create. + This corresponds to the ``webhook`` 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: + ~.gcdc_webhook.Webhook: + Webhooks host the developer's + business logic. During a session, + webhooks allow the developer to use the + data extracted by Dialogflow's natural + language processing to generate dynamic + responses, validate collected data, or + trigger actions on the backend. + + """ + # 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, webhook]) + 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 gcdc_webhook.CreateWebhookRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_webhook.CreateWebhookRequest): + request = gcdc_webhook.CreateWebhookRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if webhook is not None: + request.webhook = webhook + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_webhook] + + # 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_webhook( + self, + request: gcdc_webhook.UpdateWebhookRequest = None, + *, + webhook: gcdc_webhook.Webhook = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcdc_webhook.Webhook: + r"""Updates the specified webhook. + + Args: + request (:class:`~.gcdc_webhook.UpdateWebhookRequest`): + The request object. The request message for + [Webhooks.UpdateWebhook][google.cloud.dialogflow.cx.v3.Webhooks.UpdateWebhook]. + webhook (:class:`~.gcdc_webhook.Webhook`): + Required. The webhook to update. + This corresponds to the ``webhook`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`~.field_mask.FieldMask`): + The mask to control which fields get + updated. If the mask is not present, all + fields will be updated. + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.gcdc_webhook.Webhook: + Webhooks host the developer's + business logic. During a session, + webhooks allow the developer to use the + data extracted by Dialogflow's natural + language processing to generate dynamic + responses, validate collected data, or + trigger actions on the backend. + + """ + # 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([webhook, update_mask]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a gcdc_webhook.UpdateWebhookRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcdc_webhook.UpdateWebhookRequest): + request = gcdc_webhook.UpdateWebhookRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if webhook is not None: + request.webhook = webhook + if update_mask is not None: + request.update_mask = update_mask + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.update_webhook] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("webhook.name", request.webhook.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def delete_webhook( + self, + request: webhook.DeleteWebhookRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified webhook. + + Args: + request (:class:`~.webhook.DeleteWebhookRequest`): + The request object. The request message for + [Webhooks.DeleteWebhook][google.cloud.dialogflow.cx.v3.Webhooks.DeleteWebhook]. + name (:class:`str`): + Required. The name of the webhook to delete. Format: + ``projects//locations//agents//webhooks/``. + 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 webhook.DeleteWebhookRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, webhook.DeleteWebhookRequest): + request = webhook.DeleteWebhookRequest(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_webhook] + + # 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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("WebhooksClient",) diff --git a/google/cloud/dialogflowcx_v3/services/webhooks/pagers.py b/google/cloud/dialogflowcx_v3/services/webhooks/pagers.py new file mode 100644 index 00000000..d84e8457 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/webhooks/pagers.py @@ -0,0 +1,148 @@ +# -*- 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 + +from google.cloud.dialogflowcx_v3.types import webhook + + +class ListWebhooksPager: + """A pager for iterating through ``list_webhooks`` requests. + + This class thinly wraps an initial + :class:`~.webhook.ListWebhooksResponse` object, and + provides an ``__iter__`` method to iterate through its + ``webhooks`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListWebhooks`` requests and continue to iterate + through the ``webhooks`` field on the + corresponding responses. + + All the usual :class:`~.webhook.ListWebhooksResponse` + 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[..., webhook.ListWebhooksResponse], + request: webhook.ListWebhooksRequest, + response: webhook.ListWebhooksResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.webhook.ListWebhooksRequest`): + The initial request object. + response (:class:`~.webhook.ListWebhooksResponse`): + 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 = webhook.ListWebhooksRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[webhook.ListWebhooksResponse]: + 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[webhook.Webhook]: + for page in self.pages: + yield from page.webhooks + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListWebhooksAsyncPager: + """A pager for iterating through ``list_webhooks`` requests. + + This class thinly wraps an initial + :class:`~.webhook.ListWebhooksResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``webhooks`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListWebhooks`` requests and continue to iterate + through the ``webhooks`` field on the + corresponding responses. + + All the usual :class:`~.webhook.ListWebhooksResponse` + 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[webhook.ListWebhooksResponse]], + request: webhook.ListWebhooksRequest, + response: webhook.ListWebhooksResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (:class:`~.webhook.ListWebhooksRequest`): + The initial request object. + response (:class:`~.webhook.ListWebhooksResponse`): + 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 = webhook.ListWebhooksRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[webhook.ListWebhooksResponse]: + 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[webhook.Webhook]: + async def async_generator(): + async for page in self.pages: + for response in page.webhooks: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflowcx_v3/services/webhooks/transports/__init__.py b/google/cloud/dialogflowcx_v3/services/webhooks/transports/__init__.py new file mode 100644 index 00000000..d41753aa --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/webhooks/transports/__init__.py @@ -0,0 +1,36 @@ +# -*- 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 WebhooksTransport +from .grpc import WebhooksGrpcTransport +from .grpc_asyncio import WebhooksGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[WebhooksTransport]] +_transport_registry["grpc"] = WebhooksGrpcTransport +_transport_registry["grpc_asyncio"] = WebhooksGrpcAsyncIOTransport + + +__all__ = ( + "WebhooksTransport", + "WebhooksGrpcTransport", + "WebhooksGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflowcx_v3/services/webhooks/transports/base.py b/google/cloud/dialogflowcx_v3/services/webhooks/transports/base.py new file mode 100644 index 00000000..9cebd85a --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/webhooks/transports/base.py @@ -0,0 +1,179 @@ +# -*- 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.dialogflowcx_v3.types import webhook +from google.cloud.dialogflowcx_v3.types import webhook as gcdc_webhook +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-dialogflowcx", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class WebhooksTransport(abc.ABC): + """Abstract transport class for Webhooks.""" + + AUTH_SCOPES = ( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ) + + def __init__( + self, + *, + host: str = "dialogflow.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_webhooks: gapic_v1.method.wrap_method( + self.list_webhooks, default_timeout=None, client_info=client_info, + ), + self.get_webhook: gapic_v1.method.wrap_method( + self.get_webhook, default_timeout=None, client_info=client_info, + ), + self.create_webhook: gapic_v1.method.wrap_method( + self.create_webhook, default_timeout=None, client_info=client_info, + ), + self.update_webhook: gapic_v1.method.wrap_method( + self.update_webhook, default_timeout=None, client_info=client_info, + ), + self.delete_webhook: gapic_v1.method.wrap_method( + self.delete_webhook, default_timeout=None, client_info=client_info, + ), + } + + @property + def list_webhooks( + self, + ) -> typing.Callable[ + [webhook.ListWebhooksRequest], + typing.Union[ + webhook.ListWebhooksResponse, typing.Awaitable[webhook.ListWebhooksResponse] + ], + ]: + raise NotImplementedError() + + @property + def get_webhook( + self, + ) -> typing.Callable[ + [webhook.GetWebhookRequest], + typing.Union[webhook.Webhook, typing.Awaitable[webhook.Webhook]], + ]: + raise NotImplementedError() + + @property + def create_webhook( + self, + ) -> typing.Callable[ + [gcdc_webhook.CreateWebhookRequest], + typing.Union[gcdc_webhook.Webhook, typing.Awaitable[gcdc_webhook.Webhook]], + ]: + raise NotImplementedError() + + @property + def update_webhook( + self, + ) -> typing.Callable[ + [gcdc_webhook.UpdateWebhookRequest], + typing.Union[gcdc_webhook.Webhook, typing.Awaitable[gcdc_webhook.Webhook]], + ]: + raise NotImplementedError() + + @property + def delete_webhook( + self, + ) -> typing.Callable[ + [webhook.DeleteWebhookRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + +__all__ = ("WebhooksTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/webhooks/transports/grpc.py b/google/cloud/dialogflowcx_v3/services/webhooks/transports/grpc.py new file mode 100644 index 00000000..dac21a4d --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/webhooks/transports/grpc.py @@ -0,0 +1,363 @@ +# -*- 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.dialogflowcx_v3.types import webhook +from google.cloud.dialogflowcx_v3.types import webhook as gcdc_webhook +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import WebhooksTransport, DEFAULT_CLIENT_INFO + + +class WebhooksGrpcTransport(WebhooksTransport): + """gRPC backend transport for Webhooks. + + Service for managing + [Webhooks][google.cloud.dialogflow.cx.v3.Webhook]. + + 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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + 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 = "dialogflow.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 (Optionsl[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_webhooks( + self, + ) -> Callable[[webhook.ListWebhooksRequest], webhook.ListWebhooksResponse]: + r"""Return a callable for the list webhooks method over gRPC. + + Returns the list of all webhooks in the specified + agent. + + Returns: + Callable[[~.ListWebhooksRequest], + ~.ListWebhooksResponse]: + 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_webhooks" not in self._stubs: + self._stubs["list_webhooks"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Webhooks/ListWebhooks", + request_serializer=webhook.ListWebhooksRequest.serialize, + response_deserializer=webhook.ListWebhooksResponse.deserialize, + ) + return self._stubs["list_webhooks"] + + @property + def get_webhook(self) -> Callable[[webhook.GetWebhookRequest], webhook.Webhook]: + r"""Return a callable for the get webhook method over gRPC. + + Retrieves the specified webhook. + + Returns: + Callable[[~.GetWebhookRequest], + ~.Webhook]: + 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_webhook" not in self._stubs: + self._stubs["get_webhook"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Webhooks/GetWebhook", + request_serializer=webhook.GetWebhookRequest.serialize, + response_deserializer=webhook.Webhook.deserialize, + ) + return self._stubs["get_webhook"] + + @property + def create_webhook( + self, + ) -> Callable[[gcdc_webhook.CreateWebhookRequest], gcdc_webhook.Webhook]: + r"""Return a callable for the create webhook method over gRPC. + + Creates a webhook in the specified agent. + + Returns: + Callable[[~.CreateWebhookRequest], + ~.Webhook]: + 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_webhook" not in self._stubs: + self._stubs["create_webhook"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Webhooks/CreateWebhook", + request_serializer=gcdc_webhook.CreateWebhookRequest.serialize, + response_deserializer=gcdc_webhook.Webhook.deserialize, + ) + return self._stubs["create_webhook"] + + @property + def update_webhook( + self, + ) -> Callable[[gcdc_webhook.UpdateWebhookRequest], gcdc_webhook.Webhook]: + r"""Return a callable for the update webhook method over gRPC. + + Updates the specified webhook. + + Returns: + Callable[[~.UpdateWebhookRequest], + ~.Webhook]: + 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_webhook" not in self._stubs: + self._stubs["update_webhook"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Webhooks/UpdateWebhook", + request_serializer=gcdc_webhook.UpdateWebhookRequest.serialize, + response_deserializer=gcdc_webhook.Webhook.deserialize, + ) + return self._stubs["update_webhook"] + + @property + def delete_webhook(self) -> Callable[[webhook.DeleteWebhookRequest], empty.Empty]: + r"""Return a callable for the delete webhook method over gRPC. + + Deletes the specified webhook. + + Returns: + Callable[[~.DeleteWebhookRequest], + ~.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_webhook" not in self._stubs: + self._stubs["delete_webhook"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Webhooks/DeleteWebhook", + request_serializer=webhook.DeleteWebhookRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_webhook"] + + +__all__ = ("WebhooksGrpcTransport",) diff --git a/google/cloud/dialogflowcx_v3/services/webhooks/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3/services/webhooks/transports/grpc_asyncio.py new file mode 100644 index 00000000..16acc5a0 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/services/webhooks/transports/grpc_asyncio.py @@ -0,0 +1,373 @@ +# -*- 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.dialogflowcx_v3.types import webhook +from google.cloud.dialogflowcx_v3.types import webhook as gcdc_webhook +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import WebhooksTransport, DEFAULT_CLIENT_INFO +from .grpc import WebhooksGrpcTransport + + +class WebhooksGrpcAsyncIOTransport(WebhooksTransport): + """gRPC AsyncIO backend transport for Webhooks. + + Service for managing + [Webhooks][google.cloud.dialogflow.cx.v3.Webhook]. + + 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 = "dialogflow.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 = "dialogflow.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, + 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. + 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 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: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + 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, + ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) + + # 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_webhooks( + self, + ) -> Callable[ + [webhook.ListWebhooksRequest], Awaitable[webhook.ListWebhooksResponse] + ]: + r"""Return a callable for the list webhooks method over gRPC. + + Returns the list of all webhooks in the specified + agent. + + Returns: + Callable[[~.ListWebhooksRequest], + Awaitable[~.ListWebhooksResponse]]: + 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_webhooks" not in self._stubs: + self._stubs["list_webhooks"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Webhooks/ListWebhooks", + request_serializer=webhook.ListWebhooksRequest.serialize, + response_deserializer=webhook.ListWebhooksResponse.deserialize, + ) + return self._stubs["list_webhooks"] + + @property + def get_webhook( + self, + ) -> Callable[[webhook.GetWebhookRequest], Awaitable[webhook.Webhook]]: + r"""Return a callable for the get webhook method over gRPC. + + Retrieves the specified webhook. + + Returns: + Callable[[~.GetWebhookRequest], + Awaitable[~.Webhook]]: + 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_webhook" not in self._stubs: + self._stubs["get_webhook"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Webhooks/GetWebhook", + request_serializer=webhook.GetWebhookRequest.serialize, + response_deserializer=webhook.Webhook.deserialize, + ) + return self._stubs["get_webhook"] + + @property + def create_webhook( + self, + ) -> Callable[[gcdc_webhook.CreateWebhookRequest], Awaitable[gcdc_webhook.Webhook]]: + r"""Return a callable for the create webhook method over gRPC. + + Creates a webhook in the specified agent. + + Returns: + Callable[[~.CreateWebhookRequest], + Awaitable[~.Webhook]]: + 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_webhook" not in self._stubs: + self._stubs["create_webhook"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Webhooks/CreateWebhook", + request_serializer=gcdc_webhook.CreateWebhookRequest.serialize, + response_deserializer=gcdc_webhook.Webhook.deserialize, + ) + return self._stubs["create_webhook"] + + @property + def update_webhook( + self, + ) -> Callable[[gcdc_webhook.UpdateWebhookRequest], Awaitable[gcdc_webhook.Webhook]]: + r"""Return a callable for the update webhook method over gRPC. + + Updates the specified webhook. + + Returns: + Callable[[~.UpdateWebhookRequest], + Awaitable[~.Webhook]]: + 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_webhook" not in self._stubs: + self._stubs["update_webhook"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Webhooks/UpdateWebhook", + request_serializer=gcdc_webhook.UpdateWebhookRequest.serialize, + response_deserializer=gcdc_webhook.Webhook.deserialize, + ) + return self._stubs["update_webhook"] + + @property + def delete_webhook( + self, + ) -> Callable[[webhook.DeleteWebhookRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete webhook method over gRPC. + + Deletes the specified webhook. + + Returns: + Callable[[~.DeleteWebhookRequest], + 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_webhook" not in self._stubs: + self._stubs["delete_webhook"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.cx.v3.Webhooks/DeleteWebhook", + request_serializer=webhook.DeleteWebhookRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_webhook"] + + +__all__ = ("WebhooksGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflowcx_v3/types/__init__.py b/google/cloud/dialogflowcx_v3/types/__init__.py new file mode 100644 index 00000000..7356eaed --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/__init__.py @@ -0,0 +1,287 @@ +# -*- 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 .response_message import ResponseMessage +from .fulfillment import Fulfillment +from .page import ( + Page, + Form, + EventHandler, + TransitionRoute, + ListPagesRequest, + ListPagesResponse, + GetPageRequest, + CreatePageRequest, + UpdatePageRequest, + DeletePageRequest, +) +from .flow import ( + NluSettings, + Flow, + CreateFlowRequest, + DeleteFlowRequest, + ListFlowsRequest, + ListFlowsResponse, + GetFlowRequest, + UpdateFlowRequest, + TrainFlowRequest, +) +from .agent import ( + SpeechToTextSettings, + Agent, + ListAgentsRequest, + ListAgentsResponse, + GetAgentRequest, + CreateAgentRequest, + UpdateAgentRequest, + DeleteAgentRequest, + ExportAgentRequest, + ExportAgentResponse, + RestoreAgentRequest, +) +from .audio_config import ( + SpeechWordInfo, + InputAudioConfig, + VoiceSelectionParams, + SynthesizeSpeechConfig, + OutputAudioConfig, +) +from .entity_type import ( + EntityType, + ListEntityTypesRequest, + ListEntityTypesResponse, + GetEntityTypeRequest, + CreateEntityTypeRequest, + UpdateEntityTypeRequest, + DeleteEntityTypeRequest, +) +from .environment import ( + Environment, + ListEnvironmentsRequest, + ListEnvironmentsResponse, + GetEnvironmentRequest, + CreateEnvironmentRequest, + UpdateEnvironmentRequest, + DeleteEnvironmentRequest, + LookupEnvironmentHistoryRequest, + LookupEnvironmentHistoryResponse, +) +from .intent import ( + Intent, + ListIntentsRequest, + ListIntentsResponse, + GetIntentRequest, + CreateIntentRequest, + UpdateIntentRequest, + DeleteIntentRequest, +) +from .security_settings import ( + GetSecuritySettingsRequest, + UpdateSecuritySettingsRequest, + ListSecuritySettingsRequest, + ListSecuritySettingsResponse, + CreateSecuritySettingsRequest, + DeleteSecuritySettingsRequest, + SecuritySettings, +) +from .session_entity_type import ( + SessionEntityType, + ListSessionEntityTypesRequest, + ListSessionEntityTypesResponse, + GetSessionEntityTypeRequest, + CreateSessionEntityTypeRequest, + UpdateSessionEntityTypeRequest, + DeleteSessionEntityTypeRequest, +) +from .session import ( + DetectIntentRequest, + DetectIntentResponse, + StreamingDetectIntentRequest, + StreamingDetectIntentResponse, + StreamingRecognitionResult, + QueryParameters, + QueryInput, + QueryResult, + TextInput, + IntentInput, + AudioInput, + EventInput, + DtmfInput, + Match, + MatchIntentRequest, + MatchIntentResponse, + FulfillIntentRequest, + FulfillIntentResponse, + SentimentAnalysisResult, +) +from .transition_route_group import ( + TransitionRouteGroup, + ListTransitionRouteGroupsRequest, + ListTransitionRouteGroupsResponse, + GetTransitionRouteGroupRequest, + CreateTransitionRouteGroupRequest, + UpdateTransitionRouteGroupRequest, + DeleteTransitionRouteGroupRequest, +) +from .version import ( + CreateVersionOperationMetadata, + Version, + ListVersionsRequest, + ListVersionsResponse, + GetVersionRequest, + CreateVersionRequest, + UpdateVersionRequest, + DeleteVersionRequest, + LoadVersionRequest, +) +from .webhook import ( + Webhook, + ListWebhooksRequest, + ListWebhooksResponse, + GetWebhookRequest, + CreateWebhookRequest, + UpdateWebhookRequest, + DeleteWebhookRequest, + WebhookRequest, + WebhookResponse, + PageInfo, + SessionInfo, +) + + +__all__ = ( + "ResponseMessage", + "Fulfillment", + "Page", + "Form", + "EventHandler", + "TransitionRoute", + "ListPagesRequest", + "ListPagesResponse", + "GetPageRequest", + "CreatePageRequest", + "UpdatePageRequest", + "DeletePageRequest", + "NluSettings", + "Flow", + "CreateFlowRequest", + "DeleteFlowRequest", + "ListFlowsRequest", + "ListFlowsResponse", + "GetFlowRequest", + "UpdateFlowRequest", + "TrainFlowRequest", + "SpeechToTextSettings", + "Agent", + "ListAgentsRequest", + "ListAgentsResponse", + "GetAgentRequest", + "CreateAgentRequest", + "UpdateAgentRequest", + "DeleteAgentRequest", + "ExportAgentRequest", + "ExportAgentResponse", + "RestoreAgentRequest", + "SpeechWordInfo", + "InputAudioConfig", + "VoiceSelectionParams", + "SynthesizeSpeechConfig", + "OutputAudioConfig", + "EntityType", + "ListEntityTypesRequest", + "ListEntityTypesResponse", + "GetEntityTypeRequest", + "CreateEntityTypeRequest", + "UpdateEntityTypeRequest", + "DeleteEntityTypeRequest", + "Environment", + "ListEnvironmentsRequest", + "ListEnvironmentsResponse", + "GetEnvironmentRequest", + "CreateEnvironmentRequest", + "UpdateEnvironmentRequest", + "DeleteEnvironmentRequest", + "LookupEnvironmentHistoryRequest", + "LookupEnvironmentHistoryResponse", + "Intent", + "ListIntentsRequest", + "ListIntentsResponse", + "GetIntentRequest", + "CreateIntentRequest", + "UpdateIntentRequest", + "DeleteIntentRequest", + "GetSecuritySettingsRequest", + "UpdateSecuritySettingsRequest", + "ListSecuritySettingsRequest", + "ListSecuritySettingsResponse", + "CreateSecuritySettingsRequest", + "DeleteSecuritySettingsRequest", + "SecuritySettings", + "SessionEntityType", + "ListSessionEntityTypesRequest", + "ListSessionEntityTypesResponse", + "GetSessionEntityTypeRequest", + "CreateSessionEntityTypeRequest", + "UpdateSessionEntityTypeRequest", + "DeleteSessionEntityTypeRequest", + "DetectIntentRequest", + "DetectIntentResponse", + "StreamingDetectIntentRequest", + "StreamingDetectIntentResponse", + "StreamingRecognitionResult", + "QueryParameters", + "QueryInput", + "QueryResult", + "TextInput", + "IntentInput", + "AudioInput", + "EventInput", + "DtmfInput", + "Match", + "MatchIntentRequest", + "MatchIntentResponse", + "FulfillIntentRequest", + "FulfillIntentResponse", + "SentimentAnalysisResult", + "TransitionRouteGroup", + "ListTransitionRouteGroupsRequest", + "ListTransitionRouteGroupsResponse", + "GetTransitionRouteGroupRequest", + "CreateTransitionRouteGroupRequest", + "UpdateTransitionRouteGroupRequest", + "DeleteTransitionRouteGroupRequest", + "CreateVersionOperationMetadata", + "Version", + "ListVersionsRequest", + "ListVersionsResponse", + "GetVersionRequest", + "CreateVersionRequest", + "UpdateVersionRequest", + "DeleteVersionRequest", + "LoadVersionRequest", + "Webhook", + "ListWebhooksRequest", + "ListWebhooksResponse", + "GetWebhookRequest", + "CreateWebhookRequest", + "UpdateWebhookRequest", + "DeleteWebhookRequest", + "WebhookRequest", + "WebhookResponse", + "PageInfo", + "SessionInfo", +) diff --git a/google/cloud/dialogflowcx_v3/types/agent.py b/google/cloud/dialogflowcx_v3/types/agent.py new file mode 100644 index 00000000..9ba5bd47 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/agent.py @@ -0,0 +1,309 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import proto # type: ignore + + +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "SpeechToTextSettings", + "Agent", + "ListAgentsRequest", + "ListAgentsResponse", + "GetAgentRequest", + "CreateAgentRequest", + "UpdateAgentRequest", + "DeleteAgentRequest", + "ExportAgentRequest", + "ExportAgentResponse", + "RestoreAgentRequest", + }, +) + + +class SpeechToTextSettings(proto.Message): + r"""Settings related to speech recognition. + + Attributes: + enable_speech_adaptation (bool): + Whether to use speech adaptation for speech + recognition. + """ + + enable_speech_adaptation = proto.Field(proto.BOOL, number=1) + + +class Agent(proto.Message): + r"""Agents are best described as Natural Language Understanding (NLU) + modules that transform user requests into actionable data. You can + include agents in your app, product, or service to determine user + intent and respond to the user in a natural way. + + After you create an agent, you can add + [Intents][google.cloud.dialogflow.cx.v3.Intent], [Entity + Types][google.cloud.dialogflow.cx.v3.EntityType], + [Flows][google.cloud.dialogflow.cx.v3.Flow], + [Fulfillments][google.cloud.dialogflow.cx.v3.Fulfillment], + [Webhooks][google.cloud.dialogflow.cx.v3.Webhook], and so on to + manage the conversation flows.. + + Attributes: + name (str): + The unique identifier of the agent. Required for the + [Agents.UpdateAgent][google.cloud.dialogflow.cx.v3.Agents.UpdateAgent] + method. + [Agents.CreateAgent][google.cloud.dialogflow.cx.v3.Agents.CreateAgent] + populates the name automatically. Format: + ``projects//locations//agents/``. + display_name (str): + Required. The human-readable name of the + agent, unique within the location. + default_language_code (str): + Immutable. The default language of the agent as a language + tag. See `Language + Support `__ + for a list of the currently supported language codes. This + field cannot be set by the + [Agents.UpdateAgent][google.cloud.dialogflow.cx.v3.Agents.UpdateAgent] + method. + time_zone (str): + Required. The time zone of the agent from the `time zone + database `__, e.g., + America/New_York, Europe/Paris. + description (str): + The description of the agent. The maximum + length is 500 characters. If exceeded, the + request is rejected. + avatar_uri (str): + The URI of the agent's avatar. Avatars are used throughout + the Dialogflow console and in the self-hosted `Web + Demo `__ + integration. + speech_to_text_settings (~.gcdc_agent.SpeechToTextSettings): + Speech recognition related settings. + start_flow (str): + Immutable. Name of the start flow in this agent. A start + flow will be automatically created when the agent is + created, and can only be deleted by deleting the agent. + Format: + ``projects//locations//agents//flows/``. + enable_stackdriver_logging (bool): + Indicates if stackdriver logging is enabled + for the agent. + enable_spell_correction (bool): + Indicates if automatic spell correction is + enabled in detect intent requests. + """ + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + default_language_code = proto.Field(proto.STRING, number=3) + + time_zone = proto.Field(proto.STRING, number=5) + + description = proto.Field(proto.STRING, number=6) + + avatar_uri = proto.Field(proto.STRING, number=7) + + speech_to_text_settings = proto.Field( + proto.MESSAGE, number=13, message="SpeechToTextSettings", + ) + + start_flow = proto.Field(proto.STRING, number=16) + + enable_stackdriver_logging = proto.Field(proto.BOOL, number=18) + + enable_spell_correction = proto.Field(proto.BOOL, number=20) + + +class ListAgentsRequest(proto.Message): + r"""The request message for + [Agents.ListAgents][google.cloud.dialogflow.cx.v3.Agents.ListAgents]. + + Attributes: + parent (str): + Required. The location to list all agents for. Format: + ``projects//locations/``. + page_size (int): + The maximum number of items to return in a + single page. By default 100 and at most 1000. + page_token (str): + The next_page_token value returned from a previous list + request. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class ListAgentsResponse(proto.Message): + r"""The response message for + [Agents.ListAgents][google.cloud.dialogflow.cx.v3.Agents.ListAgents]. + + Attributes: + agents (Sequence[~.gcdc_agent.Agent]): + The list of agents. There will be a maximum number of items + returned based on the page_size field in the request. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + agents = proto.RepeatedField(proto.MESSAGE, number=1, message="Agent",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetAgentRequest(proto.Message): + r"""The request message for + [Agents.GetAgent][google.cloud.dialogflow.cx.v3.Agents.GetAgent]. + + Attributes: + name (str): + Required. The name of the agent. Format: + ``projects//locations//agents/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreateAgentRequest(proto.Message): + r"""The request message for + [Agents.CreateAgent][google.cloud.dialogflow.cx.v3.Agents.CreateAgent]. + + Attributes: + parent (str): + Required. The location to create a agent for. Format: + ``projects//locations/``. + agent (~.gcdc_agent.Agent): + Required. The agent to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + agent = proto.Field(proto.MESSAGE, number=2, message="Agent",) + + +class UpdateAgentRequest(proto.Message): + r"""The request message for + [Agents.UpdateAgent][google.cloud.dialogflow.cx.v3.Agents.UpdateAgent]. + + Attributes: + agent (~.gcdc_agent.Agent): + Required. The agent to update. + update_mask (~.field_mask.FieldMask): + The mask to control which fields get updated. + If the mask is not present, all fields will be + updated. + """ + + agent = proto.Field(proto.MESSAGE, number=1, message="Agent",) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class DeleteAgentRequest(proto.Message): + r"""The request message for + [Agents.DeleteAgent][google.cloud.dialogflow.cx.v3.Agents.DeleteAgent]. + + Attributes: + name (str): + Required. The name of the agent to delete. Format: + ``projects//locations//agents/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class ExportAgentRequest(proto.Message): + r"""The request message for + [Agents.ExportAgent][google.cloud.dialogflow.cx.v3.Agents.ExportAgent]. + + Attributes: + name (str): + Required. The name of the agent to export. Format: + ``projects//locations//agents/``. + agent_uri (str): + Optional. The `Google Cloud + Storage `__ URI to + export the agent to. The format of this URI must be + ``gs:///``. If left unspecified, + the serialized agent is returned inline. + """ + + name = proto.Field(proto.STRING, number=1) + + agent_uri = proto.Field(proto.STRING, number=2) + + +class ExportAgentResponse(proto.Message): + r"""The response message for + [Agents.ExportAgent][google.cloud.dialogflow.cx.v3.Agents.ExportAgent]. + + Attributes: + agent_uri (str): + The URI to a file containing the exported agent. This field + is populated only if ``agent_uri`` is specified in + [ExportAgentRequest][google.cloud.dialogflow.cx.v3.ExportAgentRequest]. + agent_content (bytes): + Uncompressed raw byte content for agent. + """ + + agent_uri = proto.Field(proto.STRING, number=1, oneof="agent") + + agent_content = proto.Field(proto.BYTES, number=2, oneof="agent") + + +class RestoreAgentRequest(proto.Message): + r"""The request message for + [Agents.RestoreAgent][google.cloud.dialogflow.cx.v3.Agents.RestoreAgent]. + + Attributes: + name (str): + Required. The name of the agent to restore into. Format: + ``projects//locations//agents/``. + agent_uri (str): + The `Google Cloud + Storage `__ URI to + restore agent from. The format of this URI must be + ``gs:///``. + agent_content (bytes): + Uncompressed raw byte content for agent. + """ + + name = proto.Field(proto.STRING, number=1) + + agent_uri = proto.Field(proto.STRING, number=2, oneof="agent") + + agent_content = proto.Field(proto.BYTES, number=3, oneof="agent") + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/audio_config.py b/google/cloud/dialogflowcx_v3/types/audio_config.py new file mode 100644 index 00000000..4702c9b6 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/audio_config.py @@ -0,0 +1,305 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import proto # type: ignore + + +from google.protobuf import duration_pb2 as duration # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "AudioEncoding", + "SpeechModelVariant", + "SsmlVoiceGender", + "OutputAudioEncoding", + "SpeechWordInfo", + "InputAudioConfig", + "VoiceSelectionParams", + "SynthesizeSpeechConfig", + "OutputAudioConfig", + }, +) + + +class AudioEncoding(proto.Enum): + r"""Audio encoding of the audio content sent in the conversational query + request. Refer to the `Cloud Speech API + documentation `__ + for more details. + """ + AUDIO_ENCODING_UNSPECIFIED = 0 + AUDIO_ENCODING_LINEAR_16 = 1 + AUDIO_ENCODING_FLAC = 2 + AUDIO_ENCODING_MULAW = 3 + AUDIO_ENCODING_AMR = 4 + AUDIO_ENCODING_AMR_WB = 5 + AUDIO_ENCODING_OGG_OPUS = 6 + AUDIO_ENCODING_SPEEX_WITH_HEADER_BYTE = 7 + + +class SpeechModelVariant(proto.Enum): + r"""Variant of the specified [Speech + model][google.cloud.dialogflow.cx.v3.InputAudioConfig.model] to use. + + See the `Cloud Speech + documentation `__ + for which models have different variants. For example, the + "phone_call" model has both a standard and an enhanced variant. When + you use an enhanced model, you will generally receive higher quality + results than for a standard model. + """ + SPEECH_MODEL_VARIANT_UNSPECIFIED = 0 + USE_BEST_AVAILABLE = 1 + USE_STANDARD = 2 + USE_ENHANCED = 3 + + +class SsmlVoiceGender(proto.Enum): + r"""Gender of the voice as described in `SSML voice + element `__. + """ + SSML_VOICE_GENDER_UNSPECIFIED = 0 + SSML_VOICE_GENDER_MALE = 1 + SSML_VOICE_GENDER_FEMALE = 2 + SSML_VOICE_GENDER_NEUTRAL = 3 + + +class OutputAudioEncoding(proto.Enum): + r"""Audio encoding of the output audio format in Text-To-Speech.""" + OUTPUT_AUDIO_ENCODING_UNSPECIFIED = 0 + OUTPUT_AUDIO_ENCODING_LINEAR_16 = 1 + OUTPUT_AUDIO_ENCODING_MP3 = 2 + OUTPUT_AUDIO_ENCODING_MP3_64_KBPS = 4 + OUTPUT_AUDIO_ENCODING_OGG_OPUS = 3 + OUTPUT_AUDIO_ENCODING_MULAW = 5 + + +class SpeechWordInfo(proto.Message): + r"""Information for a word recognized by the speech recognizer. + + Attributes: + word (str): + The word this info is for. + start_offset (~.duration.Duration): + Time offset relative to the beginning of the + audio that corresponds to the start of the + spoken word. This is an experimental feature and + the accuracy of the time offset can vary. + end_offset (~.duration.Duration): + Time offset relative to the beginning of the + audio that corresponds to the end of the spoken + word. This is an experimental feature and the + accuracy of the time offset can vary. + confidence (float): + The Speech confidence between 0.0 and 1.0 for + this word. A higher number indicates an + estimated greater likelihood that the recognized + word is correct. The default of 0.0 is a + sentinel value indicating that confidence was + not set. + + This field is not guaranteed to be fully stable + over time for the same audio input. Users should + also not rely on it to always be provided. + """ + + word = proto.Field(proto.STRING, number=3) + + start_offset = proto.Field(proto.MESSAGE, number=1, message=duration.Duration,) + + end_offset = proto.Field(proto.MESSAGE, number=2, message=duration.Duration,) + + confidence = proto.Field(proto.FLOAT, number=4) + + +class InputAudioConfig(proto.Message): + r"""Instructs the speech recognizer on how to process the audio + content. + + Attributes: + audio_encoding (~.audio_config.AudioEncoding): + Required. Audio encoding of the audio content + to process. + sample_rate_hertz (int): + Sample rate (in Hertz) of the audio content sent in the + query. Refer to `Cloud Speech API + documentation `__ + for more details. + enable_word_info (bool): + Optional. If ``true``, Dialogflow returns + [SpeechWordInfo][google.cloud.dialogflow.cx.v3.SpeechWordInfo] + in + [StreamingRecognitionResult][google.cloud.dialogflow.cx.v3.StreamingRecognitionResult] + with information about the recognized speech words, e.g. + start and end time offsets. If false or unspecified, Speech + doesn't return any word-level information. + phrase_hints (Sequence[str]): + Optional. A list of strings containing words and phrases + that the speech recognizer should recognize with higher + likelihood. + + See `the Cloud Speech + documentation `__ + for more details. + model (str): + Optional. Which Speech model to select for the given + request. Select the model best suited to your domain to get + best results. If a model is not explicitly specified, then + we auto-select a model based on the parameters in the + InputAudioConfig. If enhanced speech model is enabled for + the agent and an enhanced version of the specified model for + the language does not exist, then the speech is recognized + using the standard version of the specified model. Refer to + `Cloud Speech API + documentation `__ + for more details. + model_variant (~.audio_config.SpeechModelVariant): + Optional. Which variant of the [Speech + model][google.cloud.dialogflow.cx.v3.InputAudioConfig.model] + to use. + single_utterance (bool): + Optional. If ``false`` (default), recognition does not cease + until the client closes the stream. If ``true``, the + recognizer will detect a single spoken utterance in input + audio. Recognition ceases when it detects the audio's voice + has stopped or paused. In this case, once a detected intent + is received, the client should close the stream and start a + new request with a new stream as needed. Note: This setting + is relevant only for streaming methods. + """ + + audio_encoding = proto.Field(proto.ENUM, number=1, enum="AudioEncoding",) + + sample_rate_hertz = proto.Field(proto.INT32, number=2) + + enable_word_info = proto.Field(proto.BOOL, number=13) + + phrase_hints = proto.RepeatedField(proto.STRING, number=4) + + model = proto.Field(proto.STRING, number=7) + + model_variant = proto.Field(proto.ENUM, number=10, enum="SpeechModelVariant",) + + single_utterance = proto.Field(proto.BOOL, number=8) + + +class VoiceSelectionParams(proto.Message): + r"""Description of which voice to use for speech synthesis. + + Attributes: + name (str): + Optional. The name of the voice. If not set, the service + will choose a voice based on the other parameters such as + language_code and + [ssml_gender][google.cloud.dialogflow.cx.v3.VoiceSelectionParams.ssml_gender]. + ssml_gender (~.audio_config.SsmlVoiceGender): + Optional. The preferred gender of the voice. If not set, the + service will choose a voice based on the other parameters + such as language_code and + [name][google.cloud.dialogflow.cx.v3.VoiceSelectionParams.name]. + Note that this is only a preference, not requirement. If a + voice of the appropriate gender is not available, the + synthesizer should substitute a voice with a different + gender rather than failing the request. + """ + + name = proto.Field(proto.STRING, number=1) + + ssml_gender = proto.Field(proto.ENUM, number=2, enum="SsmlVoiceGender",) + + +class SynthesizeSpeechConfig(proto.Message): + r"""Configuration of how speech should be synthesized. + + Attributes: + speaking_rate (float): + Optional. Speaking rate/speed, in the range [0.25, 4.0]. 1.0 + is the normal native speed supported by the specific voice. + 2.0 is twice as fast, and 0.5 is half as fast. If + unset(0.0), defaults to the native 1.0 speed. Any other + values < 0.25 or > 4.0 will return an error. + pitch (float): + Optional. Speaking pitch, in the range [-20.0, 20.0]. 20 + means increase 20 semitones from the original pitch. -20 + means decrease 20 semitones from the original pitch. + volume_gain_db (float): + Optional. Volume gain (in dB) of the normal native volume + supported by the specific voice, in the range [-96.0, 16.0]. + If unset, or set to a value of 0.0 (dB), will play at normal + native signal amplitude. A value of -6.0 (dB) will play at + approximately half the amplitude of the normal native signal + amplitude. A value of +6.0 (dB) will play at approximately + twice the amplitude of the normal native signal amplitude. + We strongly recommend not to exceed +10 (dB) as there's + usually no effective increase in loudness for any value + greater than that. + effects_profile_id (Sequence[str]): + Optional. An identifier which selects 'audio + effects' profiles that are applied on (post + synthesized) text to speech. Effects are applied + on top of each other in the order they are + given. + voice (~.audio_config.VoiceSelectionParams): + Optional. The desired voice of the + synthesized audio. + """ + + speaking_rate = proto.Field(proto.DOUBLE, number=1) + + pitch = proto.Field(proto.DOUBLE, number=2) + + volume_gain_db = proto.Field(proto.DOUBLE, number=3) + + effects_profile_id = proto.RepeatedField(proto.STRING, number=5) + + voice = proto.Field(proto.MESSAGE, number=4, message="VoiceSelectionParams",) + + +class OutputAudioConfig(proto.Message): + r"""Instructs the speech synthesizer how to generate the output + audio content. + + Attributes: + audio_encoding (~.audio_config.OutputAudioEncoding): + Required. Audio encoding of the synthesized + audio content. + sample_rate_hertz (int): + Optional. The synthesis sample rate (in + hertz) for this audio. If not provided, then the + synthesizer will use the default sample rate + based on the audio encoding. If this is + different from the voice's natural sample rate, + then the synthesizer will honor this request by + converting to the desired sample rate (which + might result in worse audio quality). + synthesize_speech_config (~.audio_config.SynthesizeSpeechConfig): + Optional. Configuration of how speech should + be synthesized. + """ + + audio_encoding = proto.Field(proto.ENUM, number=1, enum="OutputAudioEncoding",) + + sample_rate_hertz = proto.Field(proto.INT32, number=2) + + synthesize_speech_config = proto.Field( + proto.MESSAGE, number=3, message="SynthesizeSpeechConfig", + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/entity_type.py b/google/cloud/dialogflowcx_v3/types/entity_type.py new file mode 100644 index 00000000..1322111c --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/entity_type.py @@ -0,0 +1,354 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import proto # type: ignore + + +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "EntityType", + "ListEntityTypesRequest", + "ListEntityTypesResponse", + "GetEntityTypeRequest", + "CreateEntityTypeRequest", + "UpdateEntityTypeRequest", + "DeleteEntityTypeRequest", + }, +) + + +class EntityType(proto.Message): + r"""Entities are extracted from user input and represent parameters that + are meaningful to your application. For example, a date range, a + proper name such as a geographic location or landmark, and so on. + Entities represent actionable data for your application. + + When you define an entity, you can also include synonyms that all + map to that entity. For example, "soft drink", "soda", "pop", and so + on. + + There are three types of entities: + + - **System** - entities that are defined by the Dialogflow API for + common data types such as date, time, currency, and so on. A + system entity is represented by the ``EntityType`` type. + + - **Custom** - entities that are defined by you that represent + actionable data that is meaningful to your application. For + example, you could define a ``pizza.sauce`` entity for red or + white pizza sauce, a ``pizza.cheese`` entity for the different + types of cheese on a pizza, a ``pizza.topping`` entity for + different toppings, and so on. A custom entity is represented by + the ``EntityType`` type. + + - **User** - entities that are built for an individual user such as + favorites, preferences, playlists, and so on. A user entity is + represented by the + [SessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityType] + type. + + For more information about entity types, see the `Dialogflow + documentation `__. + + Attributes: + name (str): + The unique identifier of the entity type. Required for + [EntityTypes.UpdateEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.UpdateEntityType]. + Format: + ``projects//locations//agents//entityTypes/``. + display_name (str): + Required. The human-readable name of the + entity type, unique within the agent. + kind (~.gcdc_entity_type.EntityType.Kind): + Required. Indicates the kind of entity type. + auto_expansion_mode (~.gcdc_entity_type.EntityType.AutoExpansionMode): + Indicates whether the entity type can be + automatically expanded. + entities (Sequence[~.gcdc_entity_type.EntityType.Entity]): + The collection of entity entries associated + with the entity type. + excluded_phrases (Sequence[~.gcdc_entity_type.EntityType.ExcludedPhrase]): + Collection of exceptional words and phrases that shouldn't + be matched. For example, if you have a size entity type with + entry ``giant``\ (an adjective), you might consider adding + ``giants``\ (a noun) as an exclusion. If the kind of entity + type is ``KIND_MAP``, then the phrases specified by entities + and excluded phrases should be mutually exclusive. + enable_fuzzy_extraction (bool): + Enables fuzzy entity extraction during + classification. + """ + + class Kind(proto.Enum): + r"""Represents kinds of entities.""" + KIND_UNSPECIFIED = 0 + KIND_MAP = 1 + KIND_LIST = 2 + KIND_REGEXP = 3 + + class AutoExpansionMode(proto.Enum): + r"""Represents different entity type expansion modes. Automated + expansion allows an agent to recognize values that have not been + explicitly listed in the entity (for example, new kinds of + shopping list items). + """ + AUTO_EXPANSION_MODE_UNSPECIFIED = 0 + AUTO_EXPANSION_MODE_DEFAULT = 1 + + class Entity(proto.Message): + r"""An **entity entry** for an associated entity type. Next Id = 8 + + Attributes: + value (str): + Required. The primary value associated with this entity + entry. For example, if the entity type is *vegetable*, the + value could be *scallions*. + + For ``KIND_MAP`` entity types: + + - A canonical value to be used in place of synonyms. + + For ``KIND_LIST`` entity types: + + - A string that can contain references to other entity + types (with or without aliases). + synonyms (Sequence[str]): + Required. A collection of value synonyms. For example, if + the entity type is *vegetable*, and ``value`` is + *scallions*, a synonym could be *green onions*. + + For ``KIND_LIST`` entity types: + + - This collection must contain exactly one synonym equal to + ``value``. + """ + + value = proto.Field(proto.STRING, number=1) + + synonyms = proto.RepeatedField(proto.STRING, number=2) + + class ExcludedPhrase(proto.Message): + r"""An excluded entity phrase that should not be matched. + + Attributes: + value (str): + Required. The word or phrase to be excluded. + """ + + value = proto.Field(proto.STRING, number=1) + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + kind = proto.Field(proto.ENUM, number=3, enum=Kind,) + + auto_expansion_mode = proto.Field(proto.ENUM, number=4, enum=AutoExpansionMode,) + + entities = proto.RepeatedField(proto.MESSAGE, number=5, message=Entity,) + + excluded_phrases = proto.RepeatedField( + proto.MESSAGE, number=6, message=ExcludedPhrase, + ) + + enable_fuzzy_extraction = proto.Field(proto.BOOL, number=7) + + +class ListEntityTypesRequest(proto.Message): + r"""The request message for + [EntityTypes.ListEntityTypes][google.cloud.dialogflow.cx.v3.EntityTypes.ListEntityTypes]. + + Attributes: + parent (str): + Required. The agent to list all entity types for. Format: + ``projects//locations//agents/``. + language_code (str): + The language to list entity types for. The following fields + are language dependent: + + - ``EntityType.entities.value`` + - ``EntityType.entities.synonyms`` + - ``EntityType.excluded_phrases.value`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + page_size (int): + The maximum number of items to return in a + single page. By default 100 and at most 1000. + page_token (str): + The next_page_token value returned from a previous list + request. + """ + + parent = proto.Field(proto.STRING, number=1) + + language_code = proto.Field(proto.STRING, number=2) + + page_size = proto.Field(proto.INT32, number=3) + + page_token = proto.Field(proto.STRING, number=4) + + +class ListEntityTypesResponse(proto.Message): + r"""The response message for + [EntityTypes.ListEntityTypes][google.cloud.dialogflow.cx.v3.EntityTypes.ListEntityTypes]. + + Attributes: + entity_types (Sequence[~.gcdc_entity_type.EntityType]): + The list of entity types. There will be a maximum number of + items returned based on the page_size field in the request. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + entity_types = proto.RepeatedField(proto.MESSAGE, number=1, message="EntityType",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetEntityTypeRequest(proto.Message): + r"""The request message for + [EntityTypes.GetEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.GetEntityType]. + + Attributes: + name (str): + Required. The name of the entity type. Format: + ``projects//locations//agents//entityTypes/``. + language_code (str): + The language to retrieve the entity type for. The following + fields are language dependent: + + - ``EntityType.entities.value`` + - ``EntityType.entities.synonyms`` + - ``EntityType.excluded_phrases.value`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + name = proto.Field(proto.STRING, number=1) + + language_code = proto.Field(proto.STRING, number=2) + + +class CreateEntityTypeRequest(proto.Message): + r"""The request message for + [EntityTypes.CreateEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.CreateEntityType]. + + Attributes: + parent (str): + Required. The agent to create a entity type for. Format: + ``projects//locations//agents/``. + entity_type (~.gcdc_entity_type.EntityType): + Required. The entity type to create. + language_code (str): + The language of the following fields in ``entity_type``: + + - ``EntityType.entities.value`` + - ``EntityType.entities.synonyms`` + - ``EntityType.excluded_phrases.value`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + parent = proto.Field(proto.STRING, number=1) + + entity_type = proto.Field(proto.MESSAGE, number=2, message="EntityType",) + + language_code = proto.Field(proto.STRING, number=3) + + +class UpdateEntityTypeRequest(proto.Message): + r"""The request message for + [EntityTypes.UpdateEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.UpdateEntityType]. + + Attributes: + entity_type (~.gcdc_entity_type.EntityType): + Required. The entity type to update. + language_code (str): + The language of the following fields in ``entity_type``: + + - ``EntityType.entities.value`` + - ``EntityType.entities.synonyms`` + - ``EntityType.excluded_phrases.value`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + update_mask (~.field_mask.FieldMask): + The mask to control which fields get updated. + """ + + entity_type = proto.Field(proto.MESSAGE, number=1, message="EntityType",) + + language_code = proto.Field(proto.STRING, number=2) + + update_mask = proto.Field(proto.MESSAGE, number=3, message=field_mask.FieldMask,) + + +class DeleteEntityTypeRequest(proto.Message): + r"""The request message for + [EntityTypes.DeleteEntityType][google.cloud.dialogflow.cx.v3.EntityTypes.DeleteEntityType]. + + Attributes: + name (str): + Required. The name of the entity type to delete. Format: + ``projects//locations//agents//entityTypes/``. + force (bool): + This field has no effect for entity type not being used. For + entity types that are used by intents or pages: + + - If ``force`` is set to false, an error will be returned + with message indicating the referencing resources. + - If ``force`` is set to true, Dialogflow will remove the + entity type, as well as any references to the entity type + (i.e. Page + [parameter][google.cloud.dialogflow.cx.v3.Form.Parameter] + of the entity type will be changed to '@sys.any' and + intent + [parameter][google.cloud.dialogflow.cx.v3.Intent.Parameter] + of the entity type will be removed). + """ + + name = proto.Field(proto.STRING, number=1) + + force = proto.Field(proto.BOOL, number=2) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/environment.py b/google/cloud/dialogflowcx_v3/types/environment.py new file mode 100644 index 00000000..7edb1112 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/environment.py @@ -0,0 +1,263 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import proto # type: ignore + + +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "Environment", + "ListEnvironmentsRequest", + "ListEnvironmentsResponse", + "GetEnvironmentRequest", + "CreateEnvironmentRequest", + "UpdateEnvironmentRequest", + "DeleteEnvironmentRequest", + "LookupEnvironmentHistoryRequest", + "LookupEnvironmentHistoryResponse", + }, +) + + +class Environment(proto.Message): + r"""Represents an environment for an agent. You can create + multiple versions of your agent and publish them to separate + environments. When you edit an agent, you are editing the draft + agent. At any point, you can save the draft agent as an agent + version, which is an immutable snapshot of your agent. When you + save the draft agent, it is published to the default + environment. When you create agent versions, you can publish + them to custom environments. You can create a variety of custom + environments for testing, development, production, etc. + + Attributes: + name (str): + The name of the environment. Format: + ``projects//locations//agents//environments/``. + display_name (str): + Required. The human-readable name of the + environment (unique in an agent). Limit of 64 + characters. + description (str): + The human-readable description of the + environment. The maximum length is 500 + characters. If exceeded, the request is + rejected. + version_configs (Sequence[~.gcdc_environment.Environment.VersionConfig]): + Required. A list of configurations for flow versions. You + should include version configs for all flows that are + reachable from [``Start Flow``][Agent.start_flow] in the + agent. Otherwise, an error will be returned. + update_time (~.timestamp.Timestamp): + Output only. Update time of this environment. + """ + + class VersionConfig(proto.Message): + r"""Configuration for the version. + + Attributes: + version (str): + Required. Format: projects//locations//agents//flows//versions/. + """ + + version = proto.Field(proto.STRING, number=1) + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + description = proto.Field(proto.STRING, number=3) + + version_configs = proto.RepeatedField( + proto.MESSAGE, number=6, message=VersionConfig, + ) + + update_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) + + +class ListEnvironmentsRequest(proto.Message): + r"""The request message for + [Environments.ListEnvironments][google.cloud.dialogflow.cx.v3.Environments.ListEnvironments]. + + Attributes: + parent (str): + Required. The [Agent][google.cloud.dialogflow.cx.v3.Agent] + to list all environments for. Format: + ``projects//locations//agents/``. + page_size (int): + The maximum number of items to return in a + single page. By default 20 and at most 100. + page_token (str): + The next_page_token value returned from a previous list + request. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class ListEnvironmentsResponse(proto.Message): + r"""The response message for + [Environments.ListEnvironments][google.cloud.dialogflow.cx.v3.Environments.ListEnvironments]. + + Attributes: + environments (Sequence[~.gcdc_environment.Environment]): + The list of environments. There will be a maximum number of + items returned based on the page_size field in the request. + The list may in some cases be empty or contain fewer entries + than page_size even if this isn't the last page. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + environments = proto.RepeatedField(proto.MESSAGE, number=1, message="Environment",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetEnvironmentRequest(proto.Message): + r"""The request message for + [Environments.GetEnvironment][google.cloud.dialogflow.cx.v3.Environments.GetEnvironment]. + + Attributes: + name (str): + Required. The name of the + [Environment][google.cloud.dialogflow.cx.v3.Environment]. + Format: + ``projects//locations//agents//environments/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreateEnvironmentRequest(proto.Message): + r"""The request message for + [Environments.CreateEnvironment][google.cloud.dialogflow.cx.v3.Environments.CreateEnvironment]. + + Attributes: + parent (str): + Required. The [Agent][google.cloud.dialogflow.cx.v3.Agent] + to create an + [Environment][google.cloud.dialogflow.cx.v3.Environment] + for. Format: + ``projects//locations//agents/``. + environment (~.gcdc_environment.Environment): + Required. The environment to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + environment = proto.Field(proto.MESSAGE, number=2, message="Environment",) + + +class UpdateEnvironmentRequest(proto.Message): + r"""The request message for + [Environments.UpdateEnvironment][google.cloud.dialogflow.cx.v3.Environments.UpdateEnvironment]. + + Attributes: + environment (~.gcdc_environment.Environment): + Required. The environment to update. + update_mask (~.field_mask.FieldMask): + Required. The mask to control which fields + get updated. + """ + + environment = proto.Field(proto.MESSAGE, number=1, message="Environment",) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class DeleteEnvironmentRequest(proto.Message): + r"""The request message for + [Environments.DeleteEnvironment][google.cloud.dialogflow.cx.v3.Environments.DeleteEnvironment]. + + Attributes: + name (str): + Required. The name of the + [Environment][google.cloud.dialogflow.cx.v3.Environment] to + delete. Format: + ``projects//locations//agents//environments/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class LookupEnvironmentHistoryRequest(proto.Message): + r"""The request message for + [Environments.LookupEnvironmentHistory][google.cloud.dialogflow.cx.v3.Environments.LookupEnvironmentHistory]. + + Attributes: + name (str): + Required. Resource name of the environment to look up the + history for. Format: + ``projects//locations//agents//environments/``. + page_size (int): + The maximum number of items to return in a + single page. By default 100 and at most 1000. + page_token (str): + The next_page_token value returned from a previous list + request. + """ + + name = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class LookupEnvironmentHistoryResponse(proto.Message): + r"""The response message for + [Environments.LookupEnvironmentHistory][google.cloud.dialogflow.cx.v3.Environments.LookupEnvironmentHistory]. + + Attributes: + environments (Sequence[~.gcdc_environment.Environment]): + Represents a list of snapshots for an environment. Time of + the snapshots is stored in + [``update_time``][google.cloud.dialogflow.cx.v3.Environment.update_time]. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + environments = proto.RepeatedField(proto.MESSAGE, number=1, message="Environment",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/flow.py b/google/cloud/dialogflowcx_v3/types/flow.py new file mode 100644 index 00000000..a49e12d3 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/flow.py @@ -0,0 +1,352 @@ +# -*- 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.dialogflowcx_v3.types import page +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "NluSettings", + "Flow", + "CreateFlowRequest", + "DeleteFlowRequest", + "ListFlowsRequest", + "ListFlowsResponse", + "GetFlowRequest", + "UpdateFlowRequest", + "TrainFlowRequest", + }, +) + + +class NluSettings(proto.Message): + r"""Settings related to NLU. + + Attributes: + model_type (~.gcdc_flow.NluSettings.ModelType): + Indicates the type of NLU model. + classification_threshold (float): + To filter out false positive results and + still get variety in matched natural language + inputs for your agent, you can tune the machine + learning classification threshold. If the + returned score value is less than the threshold + value, then a no-match event will be triggered. + The score values range from 0.0 (completely + uncertain) to 1.0 (completely certain). If set + to 0.0, the default of 0.3 is used. + model_training_mode (~.gcdc_flow.NluSettings.ModelTrainingMode): + Indicates NLU model training mode. + """ + + class ModelType(proto.Enum): + r"""NLU model type.""" + MODEL_TYPE_UNSPECIFIED = 0 + MODEL_TYPE_STANDARD = 1 + MODEL_TYPE_ADVANCED = 3 + + class ModelTrainingMode(proto.Enum): + r"""NLU model training mode.""" + MODEL_TRAINING_MODE_UNSPECIFIED = 0 + MODEL_TRAINING_MODE_AUTOMATIC = 1 + MODEL_TRAINING_MODE_MANUAL = 2 + + model_type = proto.Field(proto.ENUM, number=1, enum=ModelType,) + + classification_threshold = proto.Field(proto.FLOAT, number=3) + + model_training_mode = proto.Field(proto.ENUM, number=4, enum=ModelTrainingMode,) + + +class Flow(proto.Message): + r"""Flows represents the conversation flows when you build your + chatbot agent. + A flow consists of many pages connected by the transition + routes. Conversations always start with the built-in Start Flow + (with an all-0 ID). Transition routes can direct the + conversation session from the current flow (parent flow) to + another flow (sub flow). When the sub flow is finished, + Dialogflow will bring the session back to the parent flow, where + the sub flow is started. + + Usually, when a transition route is followed by a matched + intent, the intent will be "consumed". This means the intent + won't activate more transition routes. However, when the + followed transition route moves the conversation session into a + different flow, the matched intent can be carried over and to be + consumed in the target flow. + + Attributes: + name (str): + The unique identifier of the flow. Format: + ``projects//locations//agents//flows/``. + display_name (str): + Required. The human-readable name of the + flow. + description (str): + The description of the flow. The maximum + length is 500 characters. If exceeded, the + request is rejected. + transition_routes (Sequence[~.page.TransitionRoute]): + A flow's transition routes serve two purposes: + + - They are responsible for matching the user's first + utterances in the flow. + - They are inherited by every page's [transition + routes][Page.transition_routes] and can support use cases + such as the user saying "help" or "can I talk to a + human?", which can be handled in a common way regardless + of the current page. Transition routes defined in the + page have higher priority than those defined in the flow. + + TransitionRoutes are evalauted in the following order: + + - TransitionRoutes with intent specified.. + - TransitionRoutes with only condition specified. + + TransitionRoutes with intent specified are inherited by + pages in the flow. + event_handlers (Sequence[~.page.EventHandler]): + A flow's event handlers serve two purposes: + + - They are responsible for handling events (e.g. no match, + webhook errors) in the flow. + - They are inherited by every page's [event + handlers][Page.event_handlers], which can be used to + handle common events regardless of the current page. + Event handlers defined in the page have higher priority + than those defined in the flow. + + Unlike + [transition_routes][google.cloud.dialogflow.cx.v3.Flow.transition_routes], + these handlers are evaluated on a first-match basis. The + first one that matches the event get executed, with the rest + being ignored. + nlu_settings (~.gcdc_flow.NluSettings): + NLU related settings of the flow. + """ + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + description = proto.Field(proto.STRING, number=3) + + transition_routes = proto.RepeatedField( + proto.MESSAGE, number=4, message=page.TransitionRoute, + ) + + event_handlers = proto.RepeatedField( + proto.MESSAGE, number=10, message=page.EventHandler, + ) + + nlu_settings = proto.Field(proto.MESSAGE, number=11, message="NluSettings",) + + +class CreateFlowRequest(proto.Message): + r"""The request message for + [Flows.CreateFlow][google.cloud.dialogflow.cx.v3.Flows.CreateFlow]. + + Attributes: + parent (str): + Required. The agent to create a flow for. Format: + ``projects//locations//agents/``. + flow (~.gcdc_flow.Flow): + Required. The flow to create. + language_code (str): + The language of the following fields in ``flow``: + + - ``Flow.event_handlers.trigger_fulfillment.messages`` + - ``Flow.transition_routes.trigger_fulfillment.messages`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + parent = proto.Field(proto.STRING, number=1) + + flow = proto.Field(proto.MESSAGE, number=2, message="Flow",) + + language_code = proto.Field(proto.STRING, number=3) + + +class DeleteFlowRequest(proto.Message): + r"""The request message for + [Flows.DeleteFlow][google.cloud.dialogflow.cx.v3.Flows.DeleteFlow]. + + Attributes: + name (str): + Required. The name of the flow to delete. Format: + ``projects//locations//agents//flows/``. + force (bool): + This field has no effect for flows with no incoming + transitions. For flows with incoming transitions: + + - If ``force`` is set to false, an error will be returned + with message indicating the incoming transitions. + - If ``force`` is set to true, Dialogflow will remove the + flow, as well as any transitions to the flow (i.e. + [Target flow][EventHandler.target_flow] in event handlers + or [Target flow][TransitionRoute.target_flow] in + transition routes that point to this flow will be + cleared). + """ + + name = proto.Field(proto.STRING, number=1) + + force = proto.Field(proto.BOOL, number=2) + + +class ListFlowsRequest(proto.Message): + r"""The request message for + [Flows.ListFlows][google.cloud.dialogflow.cx.v3.Flows.ListFlows]. + + Attributes: + parent (str): + Required. The agent containing the flows. Format: + ``projects//locations//agents/``. + page_size (int): + The maximum number of items to return in a + single page. By default 100 and at most 1000. + page_token (str): + The next_page_token value returned from a previous list + request. + language_code (str): + The language to list flows for. The following fields are + language dependent: + + - ``Flow.event_handlers.trigger_fulfillment.messages`` + - ``Flow.transition_routes.trigger_fulfillment.messages`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + language_code = proto.Field(proto.STRING, number=4) + + +class ListFlowsResponse(proto.Message): + r"""The response message for + [Flows.ListFlows][google.cloud.dialogflow.cx.v3.Flows.ListFlows]. + + Attributes: + flows (Sequence[~.gcdc_flow.Flow]): + The list of flows. There will be a maximum number of items + returned based on the page_size field in the request. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + flows = proto.RepeatedField(proto.MESSAGE, number=1, message="Flow",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetFlowRequest(proto.Message): + r"""The response message for + [Flows.GetFlow][google.cloud.dialogflow.cx.v3.Flows.GetFlow]. + + Attributes: + name (str): + Required. The name of the flow to get. Format: + ``projects//locations//agents//flows/``. + language_code (str): + The language to retrieve the flow for. The following fields + are language dependent: + + - ``Flow.event_handlers.trigger_fulfillment.messages`` + - ``Flow.transition_routes.trigger_fulfillment.messages`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + name = proto.Field(proto.STRING, number=1) + + language_code = proto.Field(proto.STRING, number=2) + + +class UpdateFlowRequest(proto.Message): + r"""The request message for + [Flows.UpdateFlow][google.cloud.dialogflow.cx.v3.Flows.UpdateFlow]. + + Attributes: + flow (~.gcdc_flow.Flow): + Required. The flow to update. + update_mask (~.field_mask.FieldMask): + Required. The mask to control which fields get updated. If + ``update_mask`` is not specified, an error will be returned. + language_code (str): + The language of the following fields in ``flow``: + + - ``Flow.event_handlers.trigger_fulfillment.messages`` + - ``Flow.transition_routes.trigger_fulfillment.messages`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + flow = proto.Field(proto.MESSAGE, number=1, message="Flow",) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + language_code = proto.Field(proto.STRING, number=3) + + +class TrainFlowRequest(proto.Message): + r"""The request message for + [Flows.TrainFlow][google.cloud.dialogflow.cx.v3.Flows.TrainFlow]. + + Attributes: + name (str): + Required. The flow to train. Format: + ``projects//locations//agents//flows/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/fulfillment.py b/google/cloud/dialogflowcx_v3/types/fulfillment.py new file mode 100644 index 00000000..288dab10 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/fulfillment.py @@ -0,0 +1,166 @@ +# -*- 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.dialogflowcx_v3.types import response_message +from google.protobuf import struct_pb2 as struct # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", manifest={"Fulfillment",}, +) + + +class Fulfillment(proto.Message): + r"""A fulfillment can do one or more of the following actions at the + same time: + + - Generate rich message responses. + - Set parameter values. + - Call the webhook. + + Fulfillments can be called at various stages in the + [Page][google.cloud.dialogflow.cx.v3.Page] or + [Form][google.cloud.dialogflow.cx.v3.Form] lifecycle. For example, + when a + [DetectIntentRequest][google.cloud.dialogflow.cx.v3.DetectIntentRequest] + drives a session to enter a new page, the page's entry fulfillment + can add a static response to the + [QueryResult][google.cloud.dialogflow.cx.v3.QueryResult] in the + returning + [DetectIntentResponse][google.cloud.dialogflow.cx.v3.DetectIntentResponse], + call the webhook (for example, to load user data from a database), + or both. + + Attributes: + messages (Sequence[~.response_message.ResponseMessage]): + The list of rich message responses to present + to the user. + webhook (str): + The webhook to call. Format: + ``projects//locations//agents//webhooks/``. + tag (str): + The tag used by the webhook to identify which fulfillment is + being called. This field is required if ``webhook`` is + specified. + set_parameter_actions (Sequence[~.fulfillment.Fulfillment.SetParameterAction]): + Set parameter values before executing the + webhook. + conditional_cases (Sequence[~.fulfillment.Fulfillment.ConditionalCases]): + Conditional cases for this fulfillment. + """ + + class SetParameterAction(proto.Message): + r"""Setting a parameter value. + + Attributes: + parameter (str): + Display name of the parameter. + value (~.struct.Value): + The new value of the parameter. A null value + clears the parameter. + """ + + parameter = proto.Field(proto.STRING, number=1) + + value = proto.Field(proto.MESSAGE, number=2, message=struct.Value,) + + class ConditionalCases(proto.Message): + r"""A list of cascading if-else conditions. Cases are mutually + exclusive. The first one with a matching condition is selected, + all the rest ignored. + + Attributes: + cases (Sequence[~.fulfillment.Fulfillment.ConditionalCases.Case]): + A list of cascading if-else conditions. + """ + + class Case(proto.Message): + r"""Each case has a Boolean condition. When it is evaluated to be + True, the corresponding messages will be selected and evaluated + recursively. + + Attributes: + condition (str): + The condition to activate and select this case. Empty means + the condition is always true. The condition is evaluated + against [form parameters][Form.parameters] or [session + parameters][SessionInfo.parameters]. + + See the `conditions + reference `__. + case_content (Sequence[~.fulfillment.Fulfillment.ConditionalCases.Case.CaseContent]): + A list of case content. + """ + + class CaseContent(proto.Message): + r"""The list of messages or conditional cases to activate for + this case. + + Attributes: + message (~.response_message.ResponseMessage): + Returned message. + additional_cases (~.fulfillment.Fulfillment.ConditionalCases): + Additional cases to be evaluated. + """ + + message = proto.Field( + proto.MESSAGE, + number=1, + oneof="cases_or_message", + message=response_message.ResponseMessage, + ) + + additional_cases = proto.Field( + proto.MESSAGE, + number=2, + oneof="cases_or_message", + message="Fulfillment.ConditionalCases", + ) + + condition = proto.Field(proto.STRING, number=1) + + case_content = proto.RepeatedField( + proto.MESSAGE, + number=2, + message="Fulfillment.ConditionalCases.Case.CaseContent", + ) + + cases = proto.RepeatedField( + proto.MESSAGE, number=1, message="Fulfillment.ConditionalCases.Case", + ) + + messages = proto.RepeatedField( + proto.MESSAGE, number=1, message=response_message.ResponseMessage, + ) + + webhook = proto.Field(proto.STRING, number=2) + + tag = proto.Field(proto.STRING, number=3) + + set_parameter_actions = proto.RepeatedField( + proto.MESSAGE, number=4, message=SetParameterAction, + ) + + conditional_cases = proto.RepeatedField( + proto.MESSAGE, number=5, message=ConditionalCases, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/intent.py b/google/cloud/dialogflowcx_v3/types/intent.py new file mode 100644 index 00000000..446cbb1b --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/intent.py @@ -0,0 +1,396 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import proto # type: ignore + + +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "IntentView", + "Intent", + "ListIntentsRequest", + "ListIntentsResponse", + "GetIntentRequest", + "CreateIntentRequest", + "UpdateIntentRequest", + "DeleteIntentRequest", + }, +) + + +class IntentView(proto.Enum): + r"""Represents the options for views of an intent. + An intent can be a sizable object. Therefore, we provide a + resource view that does not return training phrases in the + response. + """ + INTENT_VIEW_UNSPECIFIED = 0 + INTENT_VIEW_PARTIAL = 1 + INTENT_VIEW_FULL = 2 + + +class Intent(proto.Message): + r"""An intent represents a user's intent to interact with a + conversational agent. + You can provide information for the Dialogflow API to use to + match user input to an intent by adding training phrases (i.e., + examples of user input) to your intent. + + Attributes: + name (str): + The unique identifier of the intent. Required for the + [Intents.UpdateIntent][google.cloud.dialogflow.cx.v3.Intents.UpdateIntent] + method. + [Intents.CreateIntent][google.cloud.dialogflow.cx.v3.Intents.CreateIntent] + populates the name automatically. Format: + ``projects//locations//agents//intents/``. + display_name (str): + Required. The human-readable name of the + intent, unique within the agent. + training_phrases (Sequence[~.gcdc_intent.Intent.TrainingPhrase]): + The collection of training phrases the agent + is trained on to identify the intent. + parameters (Sequence[~.gcdc_intent.Intent.Parameter]): + The collection of parameters associated with + the intent. + priority (int): + The priority of this intent. Higher numbers represent higher + priorities. + + - If the supplied value is unspecified or 0, the service + translates the value to 500,000, which corresponds to the + ``Normal`` priority in the console. + - If the supplied value is negative, the intent is ignored + in runtime detect intent requests. + is_fallback (bool): + Indicates whether this is a fallback intent. + Currently only default fallback intent is + allowed in the agent, which is added upon agent + creation. + Adding training phrases to fallback intent is + useful in the case of requests that are + mistakenly matched, since training phrases + assigned to fallback intents act as negative + examples that triggers no-match event. + labels (Sequence[~.gcdc_intent.Intent.LabelsEntry]): + Optional. The key/value metadata to label an intent. Labels + can contain lowercase letters, digits and the symbols '-' + and '_'. International characters are allowed, including + letters from unicase alphabets. Keys must start with a + letter. Keys and values can be no longer than 63 characters + and no more than 128 bytes. + + Prefix "sys." is reserved for Dialogflow defined labels. + Currently allowed Dialogflow defined labels include: + + - sys.head + - sys.contextual The above labels do not require value. + "sys.head" means the intent is a head intent. + "sys.contextual" means the intent is a contextual intent. + description (str): + Optional. Human readable description for + better understanding an intent like its scope, + content, result etc. Maximum character limit: + 140 characters. + """ + + class TrainingPhrase(proto.Message): + r"""Represents an example that the agent is trained on to + identify the intent. + + Attributes: + id (str): + Output only. The unique identifier of the + training phrase. + parts (Sequence[~.gcdc_intent.Intent.TrainingPhrase.Part]): + Required. The ordered list of training phrase parts. The + parts are concatenated in order to form the training phrase. + + Note: The API does not automatically annotate training + phrases like the Dialogflow Console does. + + Note: Do not forget to include whitespace at part + boundaries, so the training phrase is well formatted when + the parts are concatenated. + + If the training phrase does not need to be annotated with + parameters, you just need a single part with only the + [Part.text][google.cloud.dialogflow.cx.v3.Intent.TrainingPhrase.Part.text] + field set. + + If you want to annotate the training phrase, you must create + multiple parts, where the fields of each part are populated + in one of two ways: + + - ``Part.text`` is set to a part of the phrase that has no + parameters. + - ``Part.text`` is set to a part of the phrase that you + want to annotate, and the ``parameter_id`` field is set. + repeat_count (int): + Indicates how many times this example was + added to the intent. + """ + + class Part(proto.Message): + r"""Represents a part of a training phrase. + + Attributes: + text (str): + Required. The text for this part. + parameter_id (str): + The + [parameter][google.cloud.dialogflow.cx.v3.Intent.Parameter] + used to annotate this part of the training phrase. This + field is required for annotated parts of the training + phrase. + """ + + text = proto.Field(proto.STRING, number=1) + + parameter_id = proto.Field(proto.STRING, number=2) + + id = proto.Field(proto.STRING, number=1) + + parts = proto.RepeatedField( + proto.MESSAGE, number=2, message="Intent.TrainingPhrase.Part", + ) + + repeat_count = proto.Field(proto.INT32, number=3) + + class Parameter(proto.Message): + r"""Represents an intent parameter. + + Attributes: + id (str): + Required. The unique identifier of the parameter. This field + is used by [training + phrases][google.cloud.dialogflow.cx.v3.Intent.TrainingPhrase] + to annotate their + [parts][google.cloud.dialogflow.cx.v3.Intent.TrainingPhrase.Part]. + entity_type (str): + Required. The entity type of the parameter. Format: + ``projects/-/locations/-/agents/-/entityTypes/`` + for system entity types (for example, + ``projects/-/locations/-/agents/-/entityTypes/sys.date``), + or + ``projects//locations//agents//entityTypes/`` + for developer entity types. + is_list (bool): + Indicates whether the parameter represents a + list of values. + redact (bool): + Indicates whether the parameter content is + logged in text and audio. If it is set to true, + the parameter content will be replaced to + parameter id in both request and response. The + default value is false. + """ + + id = proto.Field(proto.STRING, number=1) + + entity_type = proto.Field(proto.STRING, number=2) + + is_list = proto.Field(proto.BOOL, number=3) + + redact = proto.Field(proto.BOOL, number=4) + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + training_phrases = proto.RepeatedField( + proto.MESSAGE, number=3, message=TrainingPhrase, + ) + + parameters = proto.RepeatedField(proto.MESSAGE, number=4, message=Parameter,) + + priority = proto.Field(proto.INT32, number=5) + + is_fallback = proto.Field(proto.BOOL, number=6) + + labels = proto.MapField(proto.STRING, proto.STRING, number=7) + + description = proto.Field(proto.STRING, number=8) + + +class ListIntentsRequest(proto.Message): + r"""The request message for + [Intents.ListIntents][google.cloud.dialogflow.cx.v3.Intents.ListIntents]. + + Attributes: + parent (str): + Required. The agent to list all intents for. Format: + ``projects//locations//agents/``. + language_code (str): + The language to list intents for. The following fields are + language dependent: + + - ``Intent.training_phrases.parts.text`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + intent_view (~.gcdc_intent.IntentView): + The resource view to apply to the returned + intent. + page_size (int): + The maximum number of items to return in a + single page. By default 100 and at most 1000. + page_token (str): + The next_page_token value returned from a previous list + request. + """ + + parent = proto.Field(proto.STRING, number=1) + + language_code = proto.Field(proto.STRING, number=2) + + intent_view = proto.Field(proto.ENUM, number=5, enum="IntentView",) + + page_size = proto.Field(proto.INT32, number=3) + + page_token = proto.Field(proto.STRING, number=4) + + +class ListIntentsResponse(proto.Message): + r"""The response message for + [Intents.ListIntents][google.cloud.dialogflow.cx.v3.Intents.ListIntents]. + + Attributes: + intents (Sequence[~.gcdc_intent.Intent]): + The list of intents. There will be a maximum number of items + returned based on the page_size field in the request. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + intents = proto.RepeatedField(proto.MESSAGE, number=1, message="Intent",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetIntentRequest(proto.Message): + r"""The request message for + [Intents.GetIntent][google.cloud.dialogflow.cx.v3.Intents.GetIntent]. + + Attributes: + name (str): + Required. The name of the intent. Format: + ``projects//locations//agents//intents/``. + language_code (str): + The language to retrieve the intent for. The following + fields are language dependent: + + - ``Intent.training_phrases.parts.text`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + name = proto.Field(proto.STRING, number=1) + + language_code = proto.Field(proto.STRING, number=2) + + +class CreateIntentRequest(proto.Message): + r"""The request message for + [Intents.CreateIntent][google.cloud.dialogflow.cx.v3.Intents.CreateIntent]. + + Attributes: + parent (str): + Required. The agent to create an intent for. Format: + ``projects//locations//agents/``. + intent (~.gcdc_intent.Intent): + Required. The intent to create. + language_code (str): + The language of the following fields in ``intent``: + + - ``Intent.training_phrases.parts.text`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + parent = proto.Field(proto.STRING, number=1) + + intent = proto.Field(proto.MESSAGE, number=2, message="Intent",) + + language_code = proto.Field(proto.STRING, number=3) + + +class UpdateIntentRequest(proto.Message): + r"""The request message for + [Intents.UpdateIntent][google.cloud.dialogflow.cx.v3.Intents.UpdateIntent]. + + Attributes: + intent (~.gcdc_intent.Intent): + Required. The intent to update. + language_code (str): + The language of the following fields in ``intent``: + + - ``Intent.training_phrases.parts.text`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + update_mask (~.field_mask.FieldMask): + The mask to control which fields get updated. + If the mask is not present, all fields will be + updated. + """ + + intent = proto.Field(proto.MESSAGE, number=1, message="Intent",) + + language_code = proto.Field(proto.STRING, number=2) + + update_mask = proto.Field(proto.MESSAGE, number=3, message=field_mask.FieldMask,) + + +class DeleteIntentRequest(proto.Message): + r"""The request message for + [Intents.DeleteIntent][google.cloud.dialogflow.cx.v3.Intents.DeleteIntent]. + + Attributes: + name (str): + Required. The name of the intent to delete. Format: + ``projects//locations//agents//intents/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/page.py b/google/cloud/dialogflowcx_v3/types/page.py new file mode 100644 index 00000000..a80b950e --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/page.py @@ -0,0 +1,586 @@ +# -*- 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.dialogflowcx_v3.types import fulfillment +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "Page", + "Form", + "EventHandler", + "TransitionRoute", + "ListPagesRequest", + "ListPagesResponse", + "GetPageRequest", + "CreatePageRequest", + "UpdatePageRequest", + "DeletePageRequest", + }, +) + + +class Page(proto.Message): + r"""A Dialogflow CX conversation (session) can be described and + visualized as a state machine. The states of a CX session are + represented by pages. + + For each flow, you define many pages, where your combined pages can + handle a complete conversation on the topics the flow is designed + for. At any given moment, exactly one page is the current page, the + current page is considered active, and the flow associated with that + page is considered active. Every flow has a special start page. When + a flow initially becomes active, the start page page becomes the + current page. For each conversational turn, the current page will + either stay the same or transition to another page. + + You configure each page to collect information from the end-user + that is relevant for the conversational state represented by the + page. + + For more information, see the `Page + guide `__. + + Attributes: + name (str): + The unique identifier of the page. Required for the + [Pages.UpdatePage][google.cloud.dialogflow.cx.v3.Pages.UpdatePage] + method. + [Pages.CreatePage][google.cloud.dialogflow.cx.v3.Pages.CreatePage] + populates the name automatically. Format: + ``projects//locations//agents//flows//pages/``. + display_name (str): + Required. The human-readable name of the + page, unique within the agent. + entry_fulfillment (~.fulfillment.Fulfillment): + The fulfillment to call when the session is + entering the page. + form (~.gcdc_page.Form): + The form associated with the page, used for + collecting parameters relevant to the page. + transition_route_groups (Sequence[str]): + Ordered list of + [``TransitionRouteGroups``][google.cloud.dialogflow.cx.v3.TransitionRouteGroup] + associated with the page. Transition route groups must be + unique within a page. + + - If multiple transition routes within a page scope refer + to the same intent, then the precedence order is: page's + transition route -> page's transition route group -> + flow's transition routes. + + - If multiple transition route groups within a page contain + the same intent, then the first group in the ordered list + takes precedence. + + Format:\ ``projects//locations//agents//flows//transitionRouteGroups/``. + transition_routes (Sequence[~.gcdc_page.TransitionRoute]): + A list of transitions for the transition rules of this page. + They route the conversation to another page in the same + flow, or another flow. + + When we are in a certain page, the TransitionRoutes are + evalauted in the following order: + + - TransitionRoutes defined in the page with intent + specified. + - TransitionRoutes defined in the [transition route + groups][google.cloud.dialogflow.cx.v3.Page.transition_route_groups]. + - TransitionRoutes defined in flow with intent specified. + - TransitionRoutes defined in the page with only condition + specified. + event_handlers (Sequence[~.gcdc_page.EventHandler]): + Handlers associated with the page to handle + events such as webhook errors, no match or no + input. + """ + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + entry_fulfillment = proto.Field( + proto.MESSAGE, number=7, message=fulfillment.Fulfillment, + ) + + form = proto.Field(proto.MESSAGE, number=4, message="Form",) + + transition_route_groups = proto.RepeatedField(proto.STRING, number=11) + + transition_routes = proto.RepeatedField( + proto.MESSAGE, number=9, message="TransitionRoute", + ) + + event_handlers = proto.RepeatedField( + proto.MESSAGE, number=10, message="EventHandler", + ) + + +class Form(proto.Message): + r"""A form is a data model that groups related parameters that can be + collected from the user. The process in which the agent prompts the + user and collects parameter values from the user is called form + filling. A form can be added to a + [page][google.cloud.dialogflow.cx.v3.Page]. When form filling is + done, the filled parameters will be written to the + [session][google.cloud.dialogflow.cx.v3.SessionInfo.parameters]. + + Attributes: + parameters (Sequence[~.gcdc_page.Form.Parameter]): + Parameters to collect from the user. + """ + + class Parameter(proto.Message): + r"""Represents a form parameter. + + Attributes: + display_name (str): + Required. The human-readable name of the + parameter, unique within the form. + required (bool): + Indicates whether the parameter is required. + Optional parameters will not trigger prompts; + however, they are filled if the user specifies + them. Required parameters must be filled before + form filling concludes. + entity_type (str): + Required. The entity type of the parameter. Format: + ``projects/-/locations/-/agents/-/entityTypes/`` + for system entity types (for example, + ``projects/-/locations/-/agents/-/entityTypes/sys.date``), + or + ``projects//locations//agents//entityTypes/`` + for developer entity types. + is_list (bool): + Indicates whether the parameter represents a + list of values. + fill_behavior (~.gcdc_page.Form.Parameter.FillBehavior): + Required. Defines fill behavior for the + parameter. + default_value (~.struct.Value): + The default value of an optional parameter. + If the parameter is required, the default value + will be ignored. + redact (bool): + Indicates whether the parameter content is + logged in text and audio. If it is set to true, + the parameter content will be replaced to + parameter name in both request and response. The + default value is false. + """ + + class FillBehavior(proto.Message): + r"""Configuration for how the filling of a parameter should be + handled. + + Attributes: + initial_prompt_fulfillment (~.fulfillment.Fulfillment): + Required. The fulfillment to provide the + initial prompt that the agent can present to the + user in order to fill the parameter. + reprompt_event_handlers (Sequence[~.gcdc_page.EventHandler]): + The handlers for parameter-level events, used to provide + reprompt for the parameter or transition to a different + page/flow. The supported events are: + + - ``sys.no-match-``, where N can be from 1 to 6 + - ``sys.no-match-default`` + - ``sys.no-input-``, where N can be from 1 to 6 + - ``sys.no-input-default`` + - ``sys.invalid-parameter`` + + ``initial_prompt_fulfillment`` provides the first prompt for + the parameter. + + If the user's response does not fill the parameter, a + no-match/no-input event will be triggered, and the + fulfillment associated with the + ``sys.no-match-1``/``sys.no-input-1`` handler (if defined) + will be called to provide a prompt. The + ``sys.no-match-2``/``sys.no-input-2`` handler (if defined) + will respond to the next no-match/no-input event, and so on. + + A ``sys.no-match-default`` or ``sys.no-input-default`` + handler will be used to handle all following + no-match/no-input events after all numbered + no-match/no-input handlers for the parameter are consumed. + + A ``sys.invalid-parameter`` handler can be defined to handle + the case where the parameter values have been + ``invalidated`` by webhook. For example, if the user's + response fill the parameter, however the parameter was + invalidated by webhook, the fulfillment associated with the + ``sys.invalid-parameter`` handler (if defined) will be + called to provide a prompt. + + If the event handler for the corresponding event can't be + found on the parameter, ``initial_prompt_fulfillment`` will + be re-prompted. + """ + + initial_prompt_fulfillment = proto.Field( + proto.MESSAGE, number=3, message=fulfillment.Fulfillment, + ) + + reprompt_event_handlers = proto.RepeatedField( + proto.MESSAGE, number=5, message="EventHandler", + ) + + display_name = proto.Field(proto.STRING, number=1) + + required = proto.Field(proto.BOOL, number=2) + + entity_type = proto.Field(proto.STRING, number=3) + + is_list = proto.Field(proto.BOOL, number=4) + + fill_behavior = proto.Field( + proto.MESSAGE, number=7, message="Form.Parameter.FillBehavior", + ) + + default_value = proto.Field(proto.MESSAGE, number=9, message=struct.Value,) + + redact = proto.Field(proto.BOOL, number=11) + + parameters = proto.RepeatedField(proto.MESSAGE, number=1, message=Parameter,) + + +class EventHandler(proto.Message): + r"""An event handler specifies an + [event][google.cloud.dialogflow.cx.v3.EventHandler.event] that can + be handled during a session. When the specified event happens, the + following actions are taken in order: + + - If there is a + [``trigger_fulfillment``][google.cloud.dialogflow.cx.v3.EventHandler.trigger_fulfillment] + associated with the event, it will be called. + - If there is a + [``target_page``][google.cloud.dialogflow.cx.v3.EventHandler.target_page] + associated with the event, the session will transition into the + specified page. + - If there is a + [``target_flow``][google.cloud.dialogflow.cx.v3.EventHandler.target_flow] + associated with the event, the session will transition into the + specified flow. + + Attributes: + name (str): + Output only. The unique identifier of this + event handler. + event (str): + Required. The name of the event to handle. + trigger_fulfillment (~.fulfillment.Fulfillment): + The fulfillment to call when the event + occurs. Handling webhook errors with a + fulfillment enabled with webhook could cause + infinite loop. It is invalid to specify such + fulfillment for a handler handling webhooks. + target_page (str): + The target page to transition to. Format: + ``projects//locations//agents//flows//pages/``. + target_flow (str): + The target flow to transition to. Format: + ``projects//locations//agents//flows/``. + """ + + name = proto.Field(proto.STRING, number=6) + + event = proto.Field(proto.STRING, number=4) + + trigger_fulfillment = proto.Field( + proto.MESSAGE, number=5, message=fulfillment.Fulfillment, + ) + + target_page = proto.Field(proto.STRING, number=2, oneof="target") + + target_flow = proto.Field(proto.STRING, number=3, oneof="target") + + +class TransitionRoute(proto.Message): + r"""A transition route specifies a + [intent][google.cloud.dialogflow.cx.v3.Intent] that can be matched + and/or a data condition that can be evaluated during a session. When + a specified transition is matched, the following actions are taken + in order: + + - If there is a + [``trigger_fulfillment``][google.cloud.dialogflow.cx.v3.TransitionRoute.trigger_fulfillment] + associated with the transition, it will be called. + - If there is a + [``target_page``][google.cloud.dialogflow.cx.v3.TransitionRoute.target_page] + associated with the transition, the session will transition into + the specified page. + - If there is a + [``target_flow``][google.cloud.dialogflow.cx.v3.TransitionRoute.target_flow] + associated with the transition, the session will transition into + the specified flow. + + Attributes: + name (str): + Output only. The unique identifier of this + transition route. + intent (str): + The unique identifier of an + [Intent][google.cloud.dialogflow.cx.v3.Intent]. Format: + ``projects//locations//agents//intents/``. + Indicates that the transition can only happen when the given + intent is matched. At least one of ``intent`` or + ``condition`` must be specified. When both ``intent`` and + ``condition`` are specified, the transition can only happen + when both are fulfilled. + condition (str): + The condition to evaluate against [form + parameters][google.cloud.dialogflow.cx.v3.Form.parameters] + or [session + parameters][google.cloud.dialogflow.cx.v3.SessionInfo.parameters]. + + See the `conditions + reference `__. + At least one of ``intent`` or ``condition`` must be + specified. When both ``intent`` and ``condition`` are + specified, the transition can only happen when both are + fulfilled. + trigger_fulfillment (~.fulfillment.Fulfillment): + The fulfillment to call when the condition is satisfied. At + least one of ``trigger_fulfillment`` and ``target`` must be + specified. When both are defined, ``trigger_fulfillment`` is + executed first. + target_page (str): + The target page to transition to. Format: + ``projects//locations//agents//flows//pages/``. + target_flow (str): + The target flow to transition to. Format: + ``projects//locations//agents//flows/``. + """ + + name = proto.Field(proto.STRING, number=6) + + intent = proto.Field(proto.STRING, number=1) + + condition = proto.Field(proto.STRING, number=2) + + trigger_fulfillment = proto.Field( + proto.MESSAGE, number=3, message=fulfillment.Fulfillment, + ) + + target_page = proto.Field(proto.STRING, number=4, oneof="target") + + target_flow = proto.Field(proto.STRING, number=5, oneof="target") + + +class ListPagesRequest(proto.Message): + r"""The request message for + [Pages.ListPages][google.cloud.dialogflow.cx.v3.Pages.ListPages]. + + Attributes: + parent (str): + Required. The flow to list all pages for. Format: + ``projects//locations//agents//flows/``. + language_code (str): + The language to list pages for. The following fields are + language dependent: + + - ``Page.entry_fulfillment.messages`` + - ``Page.form.parameters.fill_behavior.initial_prompt_fulfillment.messages`` + - ``Page.form.parameters.fill_behavior.reprompt_event_handlers.messages`` + - ``Page.transition_routes.trigger_fulfillment.messages`` + - + + ``Page.transition_route_groups.transition_routes.trigger_fulfillment.messages`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + page_size (int): + The maximum number of items to return in a + single page. By default 100 and at most 1000. + page_token (str): + The next_page_token value returned from a previous list + request. + """ + + parent = proto.Field(proto.STRING, number=1) + + language_code = proto.Field(proto.STRING, number=2) + + page_size = proto.Field(proto.INT32, number=3) + + page_token = proto.Field(proto.STRING, number=4) + + +class ListPagesResponse(proto.Message): + r"""The response message for + [Pages.ListPages][google.cloud.dialogflow.cx.v3.Pages.ListPages]. + + Attributes: + pages (Sequence[~.gcdc_page.Page]): + The list of pages. There will be a maximum number of items + returned based on the page_size field in the request. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + pages = proto.RepeatedField(proto.MESSAGE, number=1, message="Page",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetPageRequest(proto.Message): + r"""The request message for + [Pages.GetPage][google.cloud.dialogflow.cx.v3.Pages.GetPage]. + + Attributes: + name (str): + Required. The name of the page. Format: + ``projects//locations//agents//flows//pages/``. + language_code (str): + The language to retrieve the page for. The following fields + are language dependent: + + - ``Page.entry_fulfillment.messages`` + - ``Page.form.parameters.fill_behavior.initial_prompt_fulfillment.messages`` + - ``Page.form.parameters.fill_behavior.reprompt_event_handlers.messages`` + - ``Page.transition_routes.trigger_fulfillment.messages`` + - + + ``Page.transition_route_groups.transition_routes.trigger_fulfillment.messages`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + name = proto.Field(proto.STRING, number=1) + + language_code = proto.Field(proto.STRING, number=2) + + +class CreatePageRequest(proto.Message): + r"""The request message for + [Pages.CreatePage][google.cloud.dialogflow.cx.v3.Pages.CreatePage]. + + Attributes: + parent (str): + Required. The flow to create a page for. Format: + ``projects//locations//agents//flows/``. + page (~.gcdc_page.Page): + Required. The page to create. + language_code (str): + The language of the following fields in ``page``: + + - ``Page.entry_fulfillment.messages`` + - ``Page.form.parameters.fill_behavior.initial_prompt_fulfillment.messages`` + - ``Page.form.parameters.fill_behavior.reprompt_event_handlers.messages`` + - ``Page.transition_routes.trigger_fulfillment.messages`` + - + + ``Page.transition_route_groups.transition_routes.trigger_fulfillment.messages`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + parent = proto.Field(proto.STRING, number=1) + + page = proto.Field(proto.MESSAGE, number=2, message="Page",) + + language_code = proto.Field(proto.STRING, number=3) + + +class UpdatePageRequest(proto.Message): + r"""The request message for + [Pages.UpdatePage][google.cloud.dialogflow.cx.v3.Pages.UpdatePage]. + + Attributes: + page (~.gcdc_page.Page): + Required. The page to update. + language_code (str): + The language of the following fields in ``page``: + + - ``Page.entry_fulfillment.messages`` + - ``Page.form.parameters.fill_behavior.initial_prompt_fulfillment.messages`` + - ``Page.form.parameters.fill_behavior.reprompt_event_handlers.messages`` + - ``Page.transition_routes.trigger_fulfillment.messages`` + - + + ``Page.transition_route_groups.transition_routes.trigger_fulfillment.messages`` + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + update_mask (~.field_mask.FieldMask): + The mask to control which fields get updated. + If the mask is not present, all fields will be + updated. + """ + + page = proto.Field(proto.MESSAGE, number=1, message="Page",) + + language_code = proto.Field(proto.STRING, number=2) + + update_mask = proto.Field(proto.MESSAGE, number=3, message=field_mask.FieldMask,) + + +class DeletePageRequest(proto.Message): + r"""The request message for + [Pages.DeletePage][google.cloud.dialogflow.cx.v3.Pages.DeletePage]. + + Attributes: + name (str): + Required. The name of the page to delete. Format: + ``projects//locations//agents//Flows//pages/``. + force (bool): + This field has no effect for pages with no incoming + transitions. For pages with incoming transitions: + + - If ``force`` is set to false, an error will be returned + with message indicating the incoming transitions. + - If ``force`` is set to true, Dialogflow will remove the + page, as well as any transitions to the page (i.e. + [Target page][EventHandler.target_page] in event handlers + or [Target page][TransitionRoute.target_page] in + transition routes that point to this page will be + cleared). + """ + + name = proto.Field(proto.STRING, number=1) + + force = proto.Field(proto.BOOL, number=2) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/response_message.py b/google/cloud/dialogflowcx_v3/types/response_message.py new file mode 100644 index 00000000..837621e0 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/response_message.py @@ -0,0 +1,283 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import proto # type: ignore + + +from google.protobuf import struct_pb2 as struct # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", manifest={"ResponseMessage",}, +) + + +class ResponseMessage(proto.Message): + r"""Represents a response message that can be returned by a + conversational agent. + + Response messages are also used for output audio synthesis. The + approach is as follows: + + - If at least one OutputAudioText response is present, then all + OutputAudioText responses are linearly concatenated, and the + result is used for output audio synthesis. + - If the OutputAudioText responses are a mixture of text and SSML, + then the concatenated result is treated as SSML; otherwise, the + result is treated as either text or SSML as appropriate. The + agent designer should ideally use either text or SSML + consistently throughout the bot design. + - Otherwise, all Text responses are linearly concatenated, and the + result is used for output audio synthesis. + + This approach allows for more sophisticated user experience + scenarios, where the text displayed to the user may differ from what + is heard. + + Attributes: + text (~.response_message.ResponseMessage.Text): + Returns a text response. + payload (~.struct.Struct): + Returns a response containing a custom, + platform-specific payload. + conversation_success (~.response_message.ResponseMessage.ConversationSuccess): + Indicates that the conversation succeeded. + output_audio_text (~.response_message.ResponseMessage.OutputAudioText): + A text or ssml response that is + preferentially used for TTS output audio + synthesis, as described in the comment on the + ResponseMessage message. + live_agent_handoff (~.response_message.ResponseMessage.LiveAgentHandoff): + Hands off conversation to a human agent. + end_interaction (~.response_message.ResponseMessage.EndInteraction): + Output only. A signal that indicates the interaction with + the Dialogflow agent has ended. This message is generated by + Dialogflow only when the conversation reaches + ``END_SESSION`` or ``END_PAGE`` page. It is not supposed to + be defined by the user. It's guaranteed that there is at + most one such message in each response. + play_audio (~.response_message.ResponseMessage.PlayAudio): + Signal that the client should play an audio clip hosted at a + client-specific URI. Dialogflow uses this to construct + [mixed_audio][google.cloud.dialogflow.cx.v3.ResponseMessage.mixed_audio]. + However, Dialogflow itself does not try to read or process + the URI in any way. + mixed_audio (~.response_message.ResponseMessage.MixedAudio): + Output only. An audio response message composed of both the + synthesized Dialogflow agent responses and responses defined + via + [play_audio][google.cloud.dialogflow.cx.v3.ResponseMessage.play_audio]. + This message is generated by Dialogflow only and not + supposed to be defined by the user. + """ + + class Text(proto.Message): + r"""The text response message. + + Attributes: + text (Sequence[str]): + Required. A collection of text responses. + allow_playback_interruption (bool): + Output only. Whether the playback of this + message can be interrupted by the end user's + speech and the client can then starts the next + Dialogflow request. + """ + + text = proto.RepeatedField(proto.STRING, number=1) + + allow_playback_interruption = proto.Field(proto.BOOL, number=2) + + class LiveAgentHandoff(proto.Message): + r"""Indicates that the conversation should be handed off to a live + agent. + + Dialogflow only uses this to determine which conversations were + handed off to a human agent for measurement purposes. What else to + do with this signal is up to you and your handoff procedures. + + You may set this, for example: + + - In the + [entry_fulfillment][google.cloud.dialogflow.cx.v3.Page.entry_fulfillment] + of a [Page][google.cloud.dialogflow.cx.v3.Page] if entering the + page indicates something went extremely wrong in the + conversation. + - In a webhook response when you determine that the customer issue + can only be handled by a human. + + Attributes: + metadata (~.struct.Struct): + Custom metadata for your handoff procedure. + Dialogflow doesn't impose any structure on this. + """ + + metadata = proto.Field(proto.MESSAGE, number=1, message=struct.Struct,) + + class ConversationSuccess(proto.Message): + r"""Indicates that the conversation succeeded, i.e., the bot handled the + issue that the customer talked to it about. + + Dialogflow only uses this to determine which conversations should be + counted as successful and doesn't process the metadata in this + message in any way. Note that Dialogflow also considers + conversations that get to the conversation end page as successful + even if they don't return + [ConversationSuccess][google.cloud.dialogflow.cx.v3.ResponseMessage.ConversationSuccess]. + + You may set this, for example: + + - In the + [entry_fulfillment][google.cloud.dialogflow.cx.v3.Page.entry_fulfillment] + of a [Page][google.cloud.dialogflow.cx.v3.Page] if entering the + page indicates that the conversation succeeded. + - In a webhook response when you determine that you handled the + customer issue. + + Attributes: + metadata (~.struct.Struct): + Custom metadata. Dialogflow doesn't impose + any structure on this. + """ + + metadata = proto.Field(proto.MESSAGE, number=1, message=struct.Struct,) + + class OutputAudioText(proto.Message): + r"""A text or ssml response that is preferentially used for TTS + output audio synthesis, as described in the comment on the + ResponseMessage message. + + Attributes: + text (str): + The raw text to be synthesized. + ssml (str): + The SSML text to be synthesized. For more information, see + `SSML `__. + allow_playback_interruption (bool): + Output only. Whether the playback of this + message can be interrupted by the end user's + speech and the client can then starts the next + Dialogflow request. + """ + + text = proto.Field(proto.STRING, number=1, oneof="source") + + ssml = proto.Field(proto.STRING, number=2, oneof="source") + + allow_playback_interruption = proto.Field(proto.BOOL, number=3) + + class EndInteraction(proto.Message): + r"""Indicates that interaction with the Dialogflow agent has + ended. This message is generated by Dialogflow only and not + supposed to be defined by the user. + """ + + class PlayAudio(proto.Message): + r"""Specifies an audio clip to be played by the client as part of + the response. + + Attributes: + audio_uri (str): + Required. URI of the audio clip. Dialogflow + does not impose any validation on this value. It + is specific to the client that reads it. + allow_playback_interruption (bool): + Output only. Whether the playback of this + message can be interrupted by the end user's + speech and the client can then starts the next + Dialogflow request. + """ + + audio_uri = proto.Field(proto.STRING, number=1) + + allow_playback_interruption = proto.Field(proto.BOOL, number=2) + + class MixedAudio(proto.Message): + r"""Represents an audio message that is composed of both segments + synthesized from the Dialogflow agent prompts and ones hosted + externally at the specified URIs. The external URIs are specified + via + [play_audio][google.cloud.dialogflow.cx.v3.ResponseMessage.play_audio]. + This message is generated by Dialogflow only and not supposed to be + defined by the user. + + Attributes: + segments (Sequence[~.response_message.ResponseMessage.MixedAudio.Segment]): + Segments this audio response is composed of. + """ + + class Segment(proto.Message): + r"""Represents one segment of audio. + + Attributes: + audio (bytes): + Raw audio synthesized from the Dialogflow + agent's response using the output config + specified in the request. + uri (str): + Client-specific URI that points to an audio + clip accessible to the client. Dialogflow does + not impose any validation on it. + allow_playback_interruption (bool): + Output only. Whether the playback of this + segment can be interrupted by the end user's + speech and the client should then start the next + Dialogflow request. + """ + + audio = proto.Field(proto.BYTES, number=1, oneof="content") + + uri = proto.Field(proto.STRING, number=2, oneof="content") + + allow_playback_interruption = proto.Field(proto.BOOL, number=3) + + segments = proto.RepeatedField( + proto.MESSAGE, number=1, message="ResponseMessage.MixedAudio.Segment", + ) + + text = proto.Field(proto.MESSAGE, number=1, oneof="message", message=Text,) + + payload = proto.Field( + proto.MESSAGE, number=2, oneof="message", message=struct.Struct, + ) + + conversation_success = proto.Field( + proto.MESSAGE, number=9, oneof="message", message=ConversationSuccess, + ) + + output_audio_text = proto.Field( + proto.MESSAGE, number=8, oneof="message", message=OutputAudioText, + ) + + live_agent_handoff = proto.Field( + proto.MESSAGE, number=10, oneof="message", message=LiveAgentHandoff, + ) + + end_interaction = proto.Field( + proto.MESSAGE, number=11, oneof="message", message=EndInteraction, + ) + + play_audio = proto.Field( + proto.MESSAGE, number=12, oneof="message", message=PlayAudio, + ) + + mixed_audio = proto.Field( + proto.MESSAGE, number=13, oneof="message", message=MixedAudio, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/security_settings.py b/google/cloud/dialogflowcx_v3/types/security_settings.py new file mode 100644 index 00000000..8645b0ed --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/security_settings.py @@ -0,0 +1,223 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import proto # type: ignore + + +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "GetSecuritySettingsRequest", + "UpdateSecuritySettingsRequest", + "ListSecuritySettingsRequest", + "ListSecuritySettingsResponse", + "CreateSecuritySettingsRequest", + "DeleteSecuritySettingsRequest", + "SecuritySettings", + }, +) + + +class GetSecuritySettingsRequest(proto.Message): + r"""The request message for + [SecuritySettingsService.GetSecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettingsService.GetSecuritySettings]. + + Attributes: + name (str): + Required. Resource name of the settings. Format: + ``projects//locations//securitySettings/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class UpdateSecuritySettingsRequest(proto.Message): + r"""The request message for + [SecuritySettingsService.UpdateSecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettingsService.UpdateSecuritySettings]. + + Attributes: + security_settings (~.gcdc_security_settings.SecuritySettings): + Required. [SecuritySettings] object that contains values for + each of the fields to update. + update_mask (~.field_mask.FieldMask): + Required. The mask to control which fields + get updated. If the mask is not present, all + fields will be updated. + """ + + security_settings = proto.Field( + proto.MESSAGE, number=1, message="SecuritySettings", + ) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class ListSecuritySettingsRequest(proto.Message): + r"""The request message for [SecuritySettings.ListSecuritySettings][]. + + Attributes: + parent (str): + Required. The location to list all security settings for. + Format: ``projects//locations/``. + page_size (int): + The maximum number of items to return in a + single page. By default 20 and at most 100. + page_token (str): + The next_page_token value returned from a previous list + request. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class ListSecuritySettingsResponse(proto.Message): + r"""The response message for [SecuritySettings.ListSecuritySettings][]. + + Attributes: + security_settings (Sequence[~.gcdc_security_settings.SecuritySettings]): + The list of security settings. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + security_settings = proto.RepeatedField( + proto.MESSAGE, number=1, message="SecuritySettings", + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class CreateSecuritySettingsRequest(proto.Message): + r"""The request message for [SecuritySettings.CreateSecuritySettings][]. + + Attributes: + parent (str): + Required. The location to create an + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings] + for. Format: + ``projects//locations/``. + security_settings (~.gcdc_security_settings.SecuritySettings): + Required. The security settings to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + security_settings = proto.Field( + proto.MESSAGE, number=2, message="SecuritySettings", + ) + + +class DeleteSecuritySettingsRequest(proto.Message): + r"""The request message for [SecuritySettings.DeleteSecuritySettings][]. + + Attributes: + name (str): + Required. The name of the + [SecuritySettings][google.cloud.dialogflow.cx.v3.SecuritySettings] + to delete. Format: + ``projects//locations//securitySettings/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class SecuritySettings(proto.Message): + r"""Represents the settings related to security issues, such as + data redaction and data retention. It may take hours for updates + on the settings to propagate to all the related components and + take effect. + + Attributes: + name (str): + Required. Resource name of the settings. Format: + ``projects//locations//securitySettings/``. + display_name (str): + Required. The human-readable name of the + security settings, unique within the location. + redaction_strategy (~.gcdc_security_settings.SecuritySettings.RedactionStrategy): + Strategy that defines how we do redaction. + redaction_scope (~.gcdc_security_settings.SecuritySettings.RedactionScope): + Defines on what data we apply redaction. Note + that we don't redact data to which we don't have + access, e.g., Stackdriver logs. + inspect_template (str): + DLP inspect template name. Use this template to define + inspect base settings. + + If empty, we use the default DLP inspect config. + + The template name will have one of the following formats: + ``projects/PROJECT_ID/inspectTemplates/TEMPLATE_ID`` OR + ``organizations/ORGANIZATION_ID/inspectTemplates/TEMPLATE_ID`` + retention_window_days (int): + Retains the data for the specified number of + days. User must Set a value lower than + Dialogflow's default 30d TTL. Setting a value + higher than that has no effect. + A missing value or setting to 0 also means we + use Dialogflow's default TTL. + purge_data_types (Sequence[~.gcdc_security_settings.SecuritySettings.PurgeDataType]): + List of types of data to remove when + retention settings triggers purge. + """ + + class RedactionStrategy(proto.Enum): + r"""Defines how we redact data.""" + REDACTION_STRATEGY_UNSPECIFIED = 0 + REDACT_WITH_SERVICE = 1 + + class RedactionScope(proto.Enum): + r"""Defines what types of data to redact.""" + REDACTION_SCOPE_UNSPECIFIED = 0 + REDACT_DISK_STORAGE = 2 + + class PurgeDataType(proto.Enum): + r"""Type of data we purge after retention settings triggers + purge. + """ + PURGE_DATA_TYPE_UNSPECIFIED = 0 + DIALOGFLOW_HISTORY = 1 + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + redaction_strategy = proto.Field(proto.ENUM, number=3, enum=RedactionStrategy,) + + redaction_scope = proto.Field(proto.ENUM, number=4, enum=RedactionScope,) + + inspect_template = proto.Field(proto.STRING, number=9) + + retention_window_days = proto.Field(proto.INT32, number=6, oneof="data_retention") + + purge_data_types = proto.RepeatedField(proto.ENUM, number=8, enum=PurgeDataType,) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/session.py b/google/cloud/dialogflowcx_v3/types/session.py new file mode 100644 index 00000000..ffdc877a --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/session.py @@ -0,0 +1,903 @@ +# -*- 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.dialogflowcx_v3.types import audio_config +from google.cloud.dialogflowcx_v3.types import intent as gcdc_intent +from google.cloud.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import response_message +from google.cloud.dialogflowcx_v3.types import session_entity_type +from google.protobuf import duration_pb2 as duration # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore +from google.rpc import status_pb2 as status # type: ignore +from google.type import latlng_pb2 as latlng # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "DetectIntentRequest", + "DetectIntentResponse", + "StreamingDetectIntentRequest", + "StreamingDetectIntentResponse", + "StreamingRecognitionResult", + "QueryParameters", + "QueryInput", + "QueryResult", + "TextInput", + "IntentInput", + "AudioInput", + "EventInput", + "DtmfInput", + "Match", + "MatchIntentRequest", + "MatchIntentResponse", + "FulfillIntentRequest", + "FulfillIntentResponse", + "SentimentAnalysisResult", + }, +) + + +class DetectIntentRequest(proto.Message): + r"""The request to detect user's intent. + + Attributes: + session (str): + Required. The name of the session this query is sent to. + Format: + ``projects//locations//agents//sessions/`` + or + ``projects//locations//agents//environments//sessions/``. + If ``Environment ID`` is not specified, we assume default + 'draft' environment. It's up to the API caller to choose an + appropriate ``Session ID``. It can be a random number or + some type of session identifiers (preferably hashed). The + length of the ``Session ID`` must not exceed 36 characters. + + For more information, see the `sessions + guide `__. + query_params (~.gcdc_session.QueryParameters): + The parameters of this query. + query_input (~.gcdc_session.QueryInput): + Required. The input specification. + output_audio_config (~.audio_config.OutputAudioConfig): + Instructs the speech synthesizer how to + generate the output audio. + """ + + session = proto.Field(proto.STRING, number=1) + + query_params = proto.Field(proto.MESSAGE, number=2, message="QueryParameters",) + + query_input = proto.Field(proto.MESSAGE, number=3, message="QueryInput",) + + output_audio_config = proto.Field( + proto.MESSAGE, number=4, message=audio_config.OutputAudioConfig, + ) + + +class DetectIntentResponse(proto.Message): + r"""The message returned from the DetectIntent method. + + Attributes: + response_id (str): + Output only. The unique identifier of the + response. It can be used to locate a response in + the training example set or for reporting + issues. + query_result (~.gcdc_session.QueryResult): + The result of the conversational query. + output_audio (bytes): + The audio data bytes encoded as specified in the request. + Note: The output audio is generated based on the values of + default platform text responses found in the + [``query_result.response_messages``][google.cloud.dialogflow.cx.v3.QueryResult.response_messages] + field. If multiple default text responses exist, they will + be concatenated when generating audio. If no default + platform text responses exist, the generated audio content + will be empty. + + In some scenarios, multiple output audio fields may be + present in the response structure. In these cases, only the + top-most-level audio output has content. + output_audio_config (~.audio_config.OutputAudioConfig): + The config used by the speech synthesizer to + generate the output audio. + """ + + response_id = proto.Field(proto.STRING, number=1) + + query_result = proto.Field(proto.MESSAGE, number=2, message="QueryResult",) + + output_audio = proto.Field(proto.BYTES, number=4) + + output_audio_config = proto.Field( + proto.MESSAGE, number=5, message=audio_config.OutputAudioConfig, + ) + + +class StreamingDetectIntentRequest(proto.Message): + r"""The top-level message sent by the client to the + [Sessions.StreamingDetectIntent][google.cloud.dialogflow.cx.v3.Sessions.StreamingDetectIntent] + method. + + Multiple request messages should be sent in order: + + 1. The first message must contain + [session][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.session], + [query_input][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.query_input] + plus optionally + [query_params][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.query_params]. + If the client wants to receive an audio response, it should also + contain + [output_audio_config][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.output_audio_config]. + + 2. If + [query_input][google.cloud.dialogflow.cx.v3.StreamingDetectIntentRequest.query_input] + was set to + [query_input.audio.config][google.cloud.dialogflow.cx.v3.AudioInput.config], + all subsequent messages must contain + [query_input.audio.audio][google.cloud.dialogflow.cx.v3.AudioInput.audio] + to continue with Speech recognition. If you decide to rather + detect an intent from text input after you already started Speech + recognition, please send a message with + [query_input.text][google.cloud.dialogflow.cx.v3.QueryInput.text]. + + However, note that: + + - Dialogflow will bill you for the audio duration so far. + - Dialogflow discards all Speech recognition results in favor of + the input text. + - Dialogflow will use the language code from the first message. + + After you sent all input, you must half-close or abort the request + stream. + + Attributes: + session (str): + The name of the session this query is sent to. Format: + ``projects//locations//agents//sessions/`` + or + ``projects//locations//agents//environments//sessions/``. + If ``Environment ID`` is not specified, we assume default + 'draft' environment. It's up to the API caller to choose an + appropriate ``Session ID``. It can be a random number or + some type of session identifiers (preferably hashed). The + length of the ``Session ID`` must not exceed 36 characters. + Note: session must be set in the first request. + + For more information, see the `sessions + guide `__. + query_params (~.gcdc_session.QueryParameters): + The parameters of this query. + query_input (~.gcdc_session.QueryInput): + Required. The input specification. + output_audio_config (~.audio_config.OutputAudioConfig): + Instructs the speech synthesizer how to + generate the output audio. + """ + + session = proto.Field(proto.STRING, number=1) + + query_params = proto.Field(proto.MESSAGE, number=2, message="QueryParameters",) + + query_input = proto.Field(proto.MESSAGE, number=3, message="QueryInput",) + + output_audio_config = proto.Field( + proto.MESSAGE, number=4, message=audio_config.OutputAudioConfig, + ) + + +class StreamingDetectIntentResponse(proto.Message): + r"""The top-level message returned from the ``StreamingDetectIntent`` + method. + + Multiple response messages can be returned in order: + + 1. If the input was set to streaming audio, the first one or more + messages contain ``recognition_result``. Each + ``recognition_result`` represents a more complete transcript of + what the user said. The last ``recognition_result`` has + ``is_final`` set to ``true``. + + 2. The last message contains ``detect_intent_response``. + + Attributes: + recognition_result (~.gcdc_session.StreamingRecognitionResult): + The result of speech recognition. + detect_intent_response (~.gcdc_session.DetectIntentResponse): + The response from detect intent. + """ + + recognition_result = proto.Field( + proto.MESSAGE, number=1, oneof="response", message="StreamingRecognitionResult", + ) + + detect_intent_response = proto.Field( + proto.MESSAGE, number=2, oneof="response", message="DetectIntentResponse", + ) + + +class StreamingRecognitionResult(proto.Message): + r"""Contains a speech recognition result corresponding to a portion of + the audio that is currently being processed or an indication that + this is the end of the single requested utterance. + + Example: + + 1. transcript: "tube" + + 2. transcript: "to be a" + + 3. transcript: "to be" + + 4. transcript: "to be or not to be" is_final: true + + 5. transcript: " that's" + + 6. transcript: " that is" + + 7. message_type: ``END_OF_SINGLE_UTTERANCE`` + + 8. transcript: " that is the question" is_final: true + + Only two of the responses contain final results (#4 and #8 indicated + by ``is_final: true``). Concatenating these generates the full + transcript: "to be or not to be that is the question". + + In each response we populate: + + - for ``TRANSCRIPT``: ``transcript`` and possibly ``is_final``. + + - for ``END_OF_SINGLE_UTTERANCE``: only ``message_type``. + + Attributes: + message_type (~.gcdc_session.StreamingRecognitionResult.MessageType): + Type of the result message. + transcript (str): + Transcript text representing the words that the user spoke. + Populated if and only if ``message_type`` = ``TRANSCRIPT``. + is_final (bool): + If ``false``, the ``StreamingRecognitionResult`` represents + an interim result that may change. If ``true``, the + recognizer will not return any further hypotheses about this + piece of the audio. May only be populated for + ``message_type`` = ``TRANSCRIPT``. + confidence (float): + The Speech confidence between 0.0 and 1.0 for the current + portion of audio. A higher number indicates an estimated + greater likelihood that the recognized words are correct. + The default of 0.0 is a sentinel value indicating that + confidence was not set. + + This field is typically only provided if ``is_final`` is + true and you should not rely on it being accurate or even + set. + stability (float): + An estimate of the likelihood that the speech recognizer + will not change its guess about this interim recognition + result: + + - If the value is unspecified or 0.0, Dialogflow didn't + compute the stability. In particular, Dialogflow will + only provide stability for ``TRANSCRIPT`` results with + ``is_final = false``. + - Otherwise, the value is in (0.0, 1.0] where 0.0 means + completely unstable and 1.0 means completely stable. + speech_word_info (Sequence[~.audio_config.SpeechWordInfo]): + Word-specific information for the words recognized by Speech + in + [transcript][google.cloud.dialogflow.cx.v3.StreamingRecognitionResult.transcript]. + Populated if and only if ``message_type`` = ``TRANSCRIPT`` + and [InputAudioConfig.enable_word_info] is set. + speech_end_offset (~.duration.Duration): + Time offset of the end of this Speech recognition result + relative to the beginning of the audio. Only populated for + ``message_type`` = ``TRANSCRIPT``. + """ + + class MessageType(proto.Enum): + r"""Type of the response message.""" + MESSAGE_TYPE_UNSPECIFIED = 0 + TRANSCRIPT = 1 + END_OF_SINGLE_UTTERANCE = 2 + + message_type = proto.Field(proto.ENUM, number=1, enum=MessageType,) + + transcript = proto.Field(proto.STRING, number=2) + + is_final = proto.Field(proto.BOOL, number=3) + + confidence = proto.Field(proto.FLOAT, number=4) + + stability = proto.Field(proto.FLOAT, number=6) + + speech_word_info = proto.RepeatedField( + proto.MESSAGE, number=7, message=audio_config.SpeechWordInfo, + ) + + speech_end_offset = proto.Field(proto.MESSAGE, number=8, message=duration.Duration,) + + +class QueryParameters(proto.Message): + r"""Represents the parameters of a conversational query. + + Attributes: + time_zone (str): + The time zone of this conversational query from the `time + zone database `__, e.g., + America/New_York, Europe/Paris. If not provided, the time + zone specified in the agent is used. + geo_location (~.latlng.LatLng): + The geo location of this conversational + query. + session_entity_types (Sequence[~.session_entity_type.SessionEntityType]): + Additional session entity types to replace or + extend developer entity types with. The entity + synonyms apply to all languages and persist for + the session of this query. + payload (~.struct.Struct): + This field can be used to pass custom data + into the webhook associated with the agent. + Arbitrary JSON objects are supported. + parameters (~.struct.Struct): + Additional parameters to be put into [session + parameters][SessionInfo.parameters]. To remove a parameter + from the session, clients should explicitly set the + parameter value to null. + + Depending on your protocol or client library language, this + is a map, associative array, symbol table, dictionary, or + JSON object composed of a collection of (MapKey, MapValue) + pairs: + + - MapKey type: string + - MapKey value: parameter name + - MapValue type: + + - If parameter's entity type is a composite entity: map + - Else: string or number, depending on parameter value + type + + - MapValue value: + + - If parameter's entity type is a composite entity: map + from composite entity property names to property + values + - Else: parameter value + analyze_query_text_sentiment (bool): + Configures whether sentiment analysis should + be performed. If not provided, sentiment + analysis is not performed. + """ + + time_zone = proto.Field(proto.STRING, number=1) + + geo_location = proto.Field(proto.MESSAGE, number=2, message=latlng.LatLng,) + + session_entity_types = proto.RepeatedField( + proto.MESSAGE, number=3, message=session_entity_type.SessionEntityType, + ) + + payload = proto.Field(proto.MESSAGE, number=4, message=struct.Struct,) + + parameters = proto.Field(proto.MESSAGE, number=5, message=struct.Struct,) + + analyze_query_text_sentiment = proto.Field(proto.BOOL, number=8) + + +class QueryInput(proto.Message): + r"""Represents the query input. It can contain one of: + 1. A conversational query in the form of text. + + 2. An intent query that specifies which intent to trigger. + 3. Natural language speech audio to be processed. + + 4. An event to be triggered. + + Attributes: + text (~.gcdc_session.TextInput): + The natural language text to be processed. + intent (~.gcdc_session.IntentInput): + The intent to be triggered. + audio (~.gcdc_session.AudioInput): + The natural language speech audio to be + processed. + event (~.gcdc_session.EventInput): + The event to be triggered. + dtmf (~.gcdc_session.DtmfInput): + The DTMF event to be handled. + language_code (str): + Required. The language of the input. See `Language + Support `__ + for a list of the currently supported language codes. Note + that queries in the same session do not necessarily need to + specify the same language. + """ + + text = proto.Field(proto.MESSAGE, number=2, oneof="input", message="TextInput",) + + intent = proto.Field(proto.MESSAGE, number=3, oneof="input", message="IntentInput",) + + audio = proto.Field(proto.MESSAGE, number=5, oneof="input", message="AudioInput",) + + event = proto.Field(proto.MESSAGE, number=6, oneof="input", message="EventInput",) + + dtmf = proto.Field(proto.MESSAGE, number=7, oneof="input", message="DtmfInput",) + + language_code = proto.Field(proto.STRING, number=4) + + +class QueryResult(proto.Message): + r"""Represents the result of a conversational query. + + Attributes: + text (str): + If [natural language + text][google.cloud.dialogflow.cx.v3.TextInput] was provided + as input, this field will contain a copy of the text. + trigger_intent (str): + If an [intent][google.cloud.dialogflow.cx.v3.IntentInput] + was provided as input, this field will contain a copy of the + intent identifier. + transcript (str): + If [natural language speech + audio][google.cloud.dialogflow.cx.v3.AudioInput] was + provided as input, this field will contain the trascript for + the audio. + trigger_event (str): + If an [event][google.cloud.dialogflow.cx.v3.EventInput] was + provided as input, this field will contain the name of the + event. + language_code (str): + The language that was triggered during intent detection. See + `Language + Support `__ + for a list of the currently supported language codes. + parameters (~.struct.Struct): + The collected [session + parameters][google.cloud.dialogflow.cx.v3.SessionInfo.parameters]. + + Depending on your protocol or client library language, this + is a map, associative array, symbol table, dictionary, or + JSON object composed of a collection of (MapKey, MapValue) + pairs: + + - MapKey type: string + - MapKey value: parameter name + - MapValue type: + + - If parameter's entity type is a composite entity: map + - Else: string or number, depending on parameter value + type + + - MapValue value: + + - If parameter's entity type is a composite entity: map + from composite entity property names to property + values + - Else: parameter value + response_messages (Sequence[~.response_message.ResponseMessage]): + The list of rich messages returned to the + client. Responses vary from simple text messages + to more sophisticated, structured payloads used + to drive complex logic. + webhook_statuses (Sequence[~.status.Status]): + The list of webhook call status in the order + of call sequence. + webhook_payloads (Sequence[~.struct.Struct]): + The list of webhook payload in + [WebhookResponse.payload][google.cloud.dialogflow.cx.v3.WebhookResponse.payload], + in the order of call sequence. If some webhook call fails or + doesn't return any payload, an empty ``Struct`` would be + used instead. + current_page (~.page.Page): + The current [Page][google.cloud.dialogflow.cx.v3.Page]. + Some, not all fields are filled in this message, including + but not limited to ``name`` and ``display_name``. + intent (~.gcdc_intent.Intent): + The [Intent][google.cloud.dialogflow.cx.v3.Intent] that + matched the conversational query. Some, not all fields are + filled in this message, including but not limited to: + ``name`` and ``display_name``. This field is deprecated, + please use + [QueryResult.match][google.cloud.dialogflow.cx.v3.QueryResult.match] + instead. + intent_detection_confidence (float): + The intent detection confidence. Values range from 0.0 + (completely uncertain) to 1.0 (completely certain). This + value is for informational purpose only and is only used to + help match the best intent within the classification + threshold. This value may change for the same end-user + expression at any time due to a model retraining or change + in implementation. This field is deprecated, please use + [QueryResult.match][google.cloud.dialogflow.cx.v3.QueryResult.match] + instead. + match (~.gcdc_session.Match): + Intent match result, could be an intent or an + event. + diagnostic_info (~.struct.Struct): + The free-form diagnostic info. For example, + this field could contain webhook call latency. + The string keys of the Struct's fields map can + change without notice. + sentiment_analysis_result (~.gcdc_session.SentimentAnalysisResult): + The sentiment analyss result, which depends on + [``analyze_query_text_sentiment``] + [google.cloud.dialogflow.cx.v3.QueryParameters.analyze_query_text_sentiment], + specified in the request. + """ + + text = proto.Field(proto.STRING, number=1, oneof="query") + + trigger_intent = proto.Field(proto.STRING, number=11, oneof="query") + + transcript = proto.Field(proto.STRING, number=12, oneof="query") + + trigger_event = proto.Field(proto.STRING, number=14, oneof="query") + + language_code = proto.Field(proto.STRING, number=2) + + parameters = proto.Field(proto.MESSAGE, number=3, message=struct.Struct,) + + response_messages = proto.RepeatedField( + proto.MESSAGE, number=4, message=response_message.ResponseMessage, + ) + + webhook_statuses = proto.RepeatedField( + proto.MESSAGE, number=13, message=status.Status, + ) + + webhook_payloads = proto.RepeatedField( + proto.MESSAGE, number=6, message=struct.Struct, + ) + + current_page = proto.Field(proto.MESSAGE, number=7, message=page.Page,) + + intent = proto.Field(proto.MESSAGE, number=8, message=gcdc_intent.Intent,) + + intent_detection_confidence = proto.Field(proto.FLOAT, number=9) + + match = proto.Field(proto.MESSAGE, number=15, message="Match",) + + diagnostic_info = proto.Field(proto.MESSAGE, number=10, message=struct.Struct,) + + sentiment_analysis_result = proto.Field( + proto.MESSAGE, number=17, message="SentimentAnalysisResult", + ) + + +class TextInput(proto.Message): + r"""Represents the natural language text to be processed. + + Attributes: + text (str): + Required. The UTF-8 encoded natural language + text to be processed. Text length must not + exceed 256 characters. + """ + + text = proto.Field(proto.STRING, number=1) + + +class IntentInput(proto.Message): + r"""Represents the intent to trigger programmatically rather than + as a result of natural language processing. + + Attributes: + intent (str): + Required. The unique identifier of the intent. Format: + ``projects//locations//agents//intents/``. + """ + + intent = proto.Field(proto.STRING, number=1) + + +class AudioInput(proto.Message): + r"""Represents the natural speech audio to be processed. + + Attributes: + config (~.audio_config.InputAudioConfig): + Required. Instructs the speech recognizer how + to process the speech audio. + audio (bytes): + The natural language speech audio to be processed. A single + request can contain up to 1 minute of speech audio data. The + [transcribed + text][google.cloud.dialogflow.cx.v3.QueryResult.transcript] + cannot contain more than 256 bytes. + + For non-streaming audio detect intent, both ``config`` and + ``audio`` must be provided. For streaming audio detect + intent, ``config`` must be provided in the first request and + ``audio`` must be provided in all following requests. + """ + + config = proto.Field( + proto.MESSAGE, number=1, message=audio_config.InputAudioConfig, + ) + + audio = proto.Field(proto.BYTES, number=2) + + +class EventInput(proto.Message): + r"""Represents the event to trigger. + + Attributes: + event (str): + Name of the event. + """ + + event = proto.Field(proto.STRING, number=1) + + +class DtmfInput(proto.Message): + r"""Represents the input for dtmf event. + + Attributes: + digits (str): + The dtmf digits. + finish_digit (str): + The finish digit (if any). + """ + + digits = proto.Field(proto.STRING, number=1) + + finish_digit = proto.Field(proto.STRING, number=2) + + +class Match(proto.Message): + r"""Represents one match result of [MatchIntent][]. + + Attributes: + intent (~.gcdc_intent.Intent): + The [Intent][google.cloud.dialogflow.cx.v3.Intent] that + matched the query. Some, not all fields are filled in this + message, including but not limited to: ``name`` and + ``display_name``. Only filled for + [``INTENT``][google.cloud.dialogflow.cx.v3.Match.MatchType] + match type. + event (str): + The event that matched the query. Only filled for + [``EVENT``][google.cloud.dialogflow.cx.v3.Match.MatchType] + match type. + parameters (~.struct.Struct): + The collection of parameters extracted from + the query. + Depending on your protocol or client library + language, this is a map, associative array, + symbol table, dictionary, or JSON object + composed of a collection of (MapKey, MapValue) + pairs: + - MapKey type: string + - MapKey value: parameter name + - MapValue type: + - If parameter's entity type is a + composite entity: map - Else: string or + number, depending on parameter value type - + MapValue value: + - If parameter's entity type is a + composite entity: map from composite + entity property names to property values - + Else: parameter value + resolved_input (str): + Final text input which was matched during + MatchIntent. This value can be different from + original input sent in request because of + spelling correction or other processing. + match_type (~.gcdc_session.Match.MatchType): + Type of this [Match][google.cloud.dialogflow.cx.v3.Match]. + confidence (float): + The confidence of this match. Values range + from 0.0 (completely uncertain) to 1.0 + (completely certain). This value is for + informational purpose only and is only used to + help match the best intent within the + classification threshold. This value may change + for the same end-user expression at any time due + to a model retraining or change in + implementation. + """ + + class MatchType(proto.Enum): + r"""Type of a Match.""" + MATCH_TYPE_UNSPECIFIED = 0 + INTENT = 1 + DIRECT_INTENT = 2 + PARAMETER_FILLING = 3 + NO_MATCH = 4 + NO_INPUT = 5 + EVENT = 6 + + intent = proto.Field(proto.MESSAGE, number=1, message=gcdc_intent.Intent,) + + event = proto.Field(proto.STRING, number=6) + + parameters = proto.Field(proto.MESSAGE, number=2, message=struct.Struct,) + + resolved_input = proto.Field(proto.STRING, number=3) + + match_type = proto.Field(proto.ENUM, number=4, enum=MatchType,) + + confidence = proto.Field(proto.FLOAT, number=5) + + +class MatchIntentRequest(proto.Message): + r"""Request of [MatchIntent][]. + + Attributes: + session (str): + Required. The name of the session this query is sent to. + Format: + ``projects//locations//agents//sessions/`` + or + ``projects//locations//agents//environments//sessions/``. + If ``Environment ID`` is not specified, we assume default + 'draft' environment. It's up to the API caller to choose an + appropriate ``Session ID``. It can be a random number or + some type of session identifiers (preferably hashed). The + length of the ``Session ID`` must not exceed 36 characters. + + For more information, see the `sessions + guide `__. + query_params (~.gcdc_session.QueryParameters): + The parameters of this query. + query_input (~.gcdc_session.QueryInput): + Required. The input specification. + """ + + session = proto.Field(proto.STRING, number=1) + + query_params = proto.Field(proto.MESSAGE, number=2, message="QueryParameters",) + + query_input = proto.Field(proto.MESSAGE, number=3, message="QueryInput",) + + +class MatchIntentResponse(proto.Message): + r"""Response of [MatchIntent][]. + + Attributes: + text (str): + If [natural language + text][google.cloud.dialogflow.cx.v3.TextInput] was provided + as input, this field will contain a copy of the text. + trigger_intent (str): + If an [intent][google.cloud.dialogflow.cx.v3.IntentInput] + was provided as input, this field will contain a copy of the + intent identifier. + transcript (str): + If [natural language speech + audio][google.cloud.dialogflow.cx.v3.AudioInput] was + provided as input, this field will contain the trascript for + the audio. + trigger_event (str): + If an [event][google.cloud.dialogflow.cx.v3.EventInput] was + provided as input, this field will contain a copy of the + event name. + matches (Sequence[~.gcdc_session.Match]): + Match results, if more than one, ordered + descendingly by the confidence we have that the + particular intent matches the query. + current_page (~.page.Page): + The current [Page][google.cloud.dialogflow.cx.v3.Page]. + Some, not all fields are filled in this message, including + but not limited to ``name`` and ``display_name``. + """ + + text = proto.Field(proto.STRING, number=1, oneof="query") + + trigger_intent = proto.Field(proto.STRING, number=2, oneof="query") + + transcript = proto.Field(proto.STRING, number=3, oneof="query") + + trigger_event = proto.Field(proto.STRING, number=6, oneof="query") + + matches = proto.RepeatedField(proto.MESSAGE, number=4, message="Match",) + + current_page = proto.Field(proto.MESSAGE, number=5, message=page.Page,) + + +class FulfillIntentRequest(proto.Message): + r"""Request of [FulfillIntent][] + + Attributes: + match_intent_request (~.gcdc_session.MatchIntentRequest): + Must be same as the corresponding MatchIntent + request, otherwise the behavior is undefined. + match (~.gcdc_session.Match): + The matched intent/event to fulfill. + output_audio_config (~.audio_config.OutputAudioConfig): + Instructs the speech synthesizer how to + generate output audio. + """ + + match_intent_request = proto.Field( + proto.MESSAGE, number=1, message="MatchIntentRequest", + ) + + match = proto.Field(proto.MESSAGE, number=2, message="Match",) + + output_audio_config = proto.Field( + proto.MESSAGE, number=3, message=audio_config.OutputAudioConfig, + ) + + +class FulfillIntentResponse(proto.Message): + r"""Response of [FulfillIntent][] + + Attributes: + response_id (str): + Output only. The unique identifier of the + response. It can be used to locate a response in + the training example set or for reporting + issues. + query_result (~.gcdc_session.QueryResult): + The result of the conversational query. + output_audio (bytes): + The audio data bytes encoded as specified in the request. + Note: The output audio is generated based on the values of + default platform text responses found in the + [``query_result.response_messages``][google.cloud.dialogflow.cx.v3.QueryResult.response_messages] + field. If multiple default text responses exist, they will + be concatenated when generating audio. If no default + platform text responses exist, the generated audio content + will be empty. + + In some scenarios, multiple output audio fields may be + present in the response structure. In these cases, only the + top-most-level audio output has content. + output_audio_config (~.audio_config.OutputAudioConfig): + The config used by the speech synthesizer to + generate the output audio. + """ + + response_id = proto.Field(proto.STRING, number=1) + + query_result = proto.Field(proto.MESSAGE, number=2, message="QueryResult",) + + output_audio = proto.Field(proto.BYTES, number=3) + + output_audio_config = proto.Field( + proto.MESSAGE, number=4, message=audio_config.OutputAudioConfig, + ) + + +class SentimentAnalysisResult(proto.Message): + r"""The result of sentiment analysis. Sentiment analysis inspects + user input and identifies the prevailing subjective opinion, + especially to determine a user's attitude as positive, negative, + or neutral. + + Attributes: + score (float): + Sentiment score between -1.0 (negative + sentiment) and 1.0 (positive sentiment). + magnitude (float): + A non-negative number in the [0, +inf) range, which + represents the absolute magnitude of sentiment, regardless + of score (positive or negative). + """ + + score = proto.Field(proto.FLOAT, number=1) + + magnitude = proto.Field(proto.FLOAT, number=2) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/session_entity_type.py b/google/cloud/dialogflowcx_v3/types/session_entity_type.py new file mode 100644 index 00000000..b3a45290 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/session_entity_type.py @@ -0,0 +1,225 @@ +# -*- 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.dialogflowcx_v3.types import entity_type +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "SessionEntityType", + "ListSessionEntityTypesRequest", + "ListSessionEntityTypesResponse", + "GetSessionEntityTypeRequest", + "CreateSessionEntityTypeRequest", + "UpdateSessionEntityTypeRequest", + "DeleteSessionEntityTypeRequest", + }, +) + + +class SessionEntityType(proto.Message): + r"""Session entity types are referred to as **User** entity types and + are entities that are built for an individual user such as + favorites, preferences, playlists, and so on. + + You can redefine a session entity type at the session level to + extend or replace a [custom entity + type][google.cloud.dialogflow.cx.v3.EntityType] at the user session + level (we refer to the entity types defined at the agent level as + "custom entity types"). + + Note: session entity types apply to all queries, regardless of the + language. + + For more information about entity types, see the `Dialogflow + documentation `__. + + Attributes: + name (str): + Required. The unique identifier of the session entity type. + Format: + ``projects//locations//agents//sessions//entityTypes/`` + or + ``projects//locations//agents//environments//sessions//entityTypes/``. + If ``Environment ID`` is not specified, we assume default + 'draft' environment. + entity_override_mode (~.gcdc_session_entity_type.SessionEntityType.EntityOverrideMode): + Required. Indicates whether the additional + data should override or supplement the custom + entity type definition. + entities (Sequence[~.entity_type.EntityType.Entity]): + Required. The collection of entities to + override or supplement the custom entity type. + """ + + class EntityOverrideMode(proto.Enum): + r"""The types of modifications for the session entity type.""" + ENTITY_OVERRIDE_MODE_UNSPECIFIED = 0 + ENTITY_OVERRIDE_MODE_OVERRIDE = 1 + ENTITY_OVERRIDE_MODE_SUPPLEMENT = 2 + + name = proto.Field(proto.STRING, number=1) + + entity_override_mode = proto.Field(proto.ENUM, number=3, enum=EntityOverrideMode,) + + entities = proto.RepeatedField( + proto.MESSAGE, number=4, message=entity_type.EntityType.Entity, + ) + + +class ListSessionEntityTypesRequest(proto.Message): + r"""The request message for + [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.cx.v3.SessionEntityTypes.ListSessionEntityTypes]. + + Attributes: + parent (str): + Required. The session to list all session entity types from. + Format: + ``projects//locations//agents//sessions/`` + or + ``projects//locations//agents//environments//sessions/``. + If ``Environment ID`` is not specified, we assume default + 'draft' environment. + page_size (int): + The maximum number of items to return in a + single page. By default 100 and at most 1000. + page_token (str): + The next_page_token value returned from a previous list + request. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class ListSessionEntityTypesResponse(proto.Message): + r"""The response message for + [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.cx.v3.SessionEntityTypes.ListSessionEntityTypes]. + + Attributes: + session_entity_types (Sequence[~.gcdc_session_entity_type.SessionEntityType]): + The list of session entity types. There will be a maximum + number of items returned based on the page_size field in the + request. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + session_entity_types = proto.RepeatedField( + proto.MESSAGE, number=1, message="SessionEntityType", + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetSessionEntityTypeRequest(proto.Message): + r"""The request message for + [SessionEntityTypes.GetSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.GetSessionEntityType]. + + Attributes: + name (str): + Required. The name of the session entity type. Format: + ``projects//locations//agents//sessions//entityTypes/`` + or + ``projects//locations//agents//environments//sessions//entityTypes/``. + If ``Environment ID`` is not specified, we assume default + 'draft' environment. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreateSessionEntityTypeRequest(proto.Message): + r"""The request message for + [SessionEntityTypes.CreateSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.CreateSessionEntityType]. + + Attributes: + parent (str): + Required. The session to create a session entity type for. + Format: + ``projects//locations//agents//sessions/`` + or + ``projects//locations//agents//environments//sessions/``. + If ``Environment ID`` is not specified, we assume default + 'draft' environment. + session_entity_type (~.gcdc_session_entity_type.SessionEntityType): + Required. The session entity type to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + session_entity_type = proto.Field( + proto.MESSAGE, number=2, message="SessionEntityType", + ) + + +class UpdateSessionEntityTypeRequest(proto.Message): + r"""The request message for + [SessionEntityTypes.UpdateSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.UpdateSessionEntityType]. + + Attributes: + session_entity_type (~.gcdc_session_entity_type.SessionEntityType): + Required. The session entity type to update. Format: + ``projects//locations//agents//sessions//entityTypes/`` + or + ``projects//locations//agents//environments//sessions//entityTypes/``. + If ``Environment ID`` is not specified, we assume default + 'draft' environment. + update_mask (~.field_mask.FieldMask): + The mask to control which fields get updated. + """ + + session_entity_type = proto.Field( + proto.MESSAGE, number=1, message="SessionEntityType", + ) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class DeleteSessionEntityTypeRequest(proto.Message): + r"""The request message for + [SessionEntityTypes.DeleteSessionEntityType][google.cloud.dialogflow.cx.v3.SessionEntityTypes.DeleteSessionEntityType]. + + Attributes: + name (str): + Required. The name of the session entity type to delete. + Format: + ``projects//locations//agents//sessions//entityTypes/`` + or + ``projects//locations//agents//environments//sessions//entityTypes/``. + If ``Environment ID`` is not specified, we assume default + 'draft' environment. + """ + + name = proto.Field(proto.STRING, number=1) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/transition_route_group.py b/google/cloud/dialogflowcx_v3/types/transition_route_group.py new file mode 100644 index 00000000..a6a838a3 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/transition_route_group.py @@ -0,0 +1,267 @@ +# -*- 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.dialogflowcx_v3.types import page +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "TransitionRouteGroup", + "ListTransitionRouteGroupsRequest", + "ListTransitionRouteGroupsResponse", + "GetTransitionRouteGroupRequest", + "CreateTransitionRouteGroupRequest", + "UpdateTransitionRouteGroupRequest", + "DeleteTransitionRouteGroupRequest", + }, +) + + +class TransitionRouteGroup(proto.Message): + r"""An TransitionRouteGroup represents a group of + [``TransitionRoutes``][google.cloud.dialogflow.cx.v3.TransitionRoute] + to be used by a [Page][google.cloud.dialogflow.cx.v3.Page]. + + Attributes: + name (str): + The unique identifier of the transition route group. + [TransitionRouteGroups.CreateTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.CreateTransitionRouteGroup] + populates the name automatically. Format: + ``projects//locations//agents//flows//transitionRouteGroups/``. + display_name (str): + Required. The human-readable name of the transition route + group, unique within the + [Agent][google.cloud.dialogflow.cx.v3.Agent]. The display + name can be no longer than 30 characters. + transition_routes (Sequence[~.page.TransitionRoute]): + Transition routes associated with the + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + Duplicate transition routes (i.e. using the same + [``intent``][google.cloud.dialogflow.cx.v3.TransitionRoute.intent]) + are not allowed. + + Note that the + [``name``][google.cloud.dialogflow.cx.v3.TransitionRoute.name] + field is not used in the transition route group scope. + """ + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + transition_routes = proto.RepeatedField( + proto.MESSAGE, number=5, message=page.TransitionRoute, + ) + + +class ListTransitionRouteGroupsRequest(proto.Message): + r"""The request message for + [TransitionRouteGroups.ListTransitionRouteGroups][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.ListTransitionRouteGroups]. + + Attributes: + parent (str): + Required. The flow to list all transition route groups for. + Format: + ``projects//locations//agents//flows/``. + page_size (int): + The maximum number of items to return in a + single page. By default 100 and at most 1000. + page_token (str): + The next_page_token value returned from a previous list + request. + language_code (str): + The language to list transition route groups for. The field + [``messages``][TransitionRoute.trigger_fulfillment.messages] + in + [TransitionRoute][google.cloud.dialogflow.cx.v3.TransitionRoute] + is language dependent. + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + language_code = proto.Field(proto.STRING, number=4) + + +class ListTransitionRouteGroupsResponse(proto.Message): + r"""The response message for + [TransitionRouteGroups.ListTransitionRouteGroups][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.ListTransitionRouteGroups]. + + Attributes: + transition_route_groups (Sequence[~.gcdc_transition_route_group.TransitionRouteGroup]): + The list of transition route groups. There will be a maximum + number of items returned based on the page_size field in the + request. The list may in some cases be empty or contain + fewer entries than page_size even if this isn't the last + page. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + transition_route_groups = proto.RepeatedField( + proto.MESSAGE, number=1, message="TransitionRouteGroup", + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetTransitionRouteGroupRequest(proto.Message): + r"""The request message for + [TransitionRouteGroups.GetTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.GetTransitionRouteGroup]. + + Attributes: + name (str): + Required. The name of the + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup]. + Format: + ``projects//locations//agents//flows//transitionRouteGroups/``. + language_code (str): + The language to list transition route groups for. The field + [``messages``][TransitionRoute.trigger_fulfillment.messages] + in + [TransitionRoute][google.cloud.dialogflow.cx.v3.TransitionRoute] + is language dependent. + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + name = proto.Field(proto.STRING, number=1) + + language_code = proto.Field(proto.STRING, number=2) + + +class CreateTransitionRouteGroupRequest(proto.Message): + r"""The request message for + [TransitionRouteGroups.CreateTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.CreateTransitionRouteGroup]. + + Attributes: + parent (str): + Required. The flow to create an + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup] + for. Format: + ``projects//locations//agents//flows/``. + transition_route_group (~.gcdc_transition_route_group.TransitionRouteGroup): + Required. The transition route group to + create. + language_code (str): + The language to list transition route groups for. The field + [``messages``][TransitionRoute.trigger_fulfillment.messages] + in + [TransitionRoute][google.cloud.dialogflow.cx.v3.TransitionRoute] + is language dependent. + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + parent = proto.Field(proto.STRING, number=1) + + transition_route_group = proto.Field( + proto.MESSAGE, number=2, message="TransitionRouteGroup", + ) + + language_code = proto.Field(proto.STRING, number=3) + + +class UpdateTransitionRouteGroupRequest(proto.Message): + r"""The request message for + [TransitionRouteGroups.UpdateTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.UpdateTransitionRouteGroup]. + + Attributes: + transition_route_group (~.gcdc_transition_route_group.TransitionRouteGroup): + Required. The transition route group to + update. + update_mask (~.field_mask.FieldMask): + The mask to control which fields get updated. + language_code (str): + The language to list transition route groups for. The field + [``messages``][TransitionRoute.trigger_fulfillment.messages] + in + [TransitionRoute][google.cloud.dialogflow.cx.v3.TransitionRoute] + is language dependent. + + If not specified, the agent's default language is used. + `Many + languages `__ + are supported. Note: languages must be enabled in the agent + before they can be used. + """ + + transition_route_group = proto.Field( + proto.MESSAGE, number=1, message="TransitionRouteGroup", + ) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + language_code = proto.Field(proto.STRING, number=3) + + +class DeleteTransitionRouteGroupRequest(proto.Message): + r"""The request message for + [TransitionRouteGroups.DeleteTransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroups.DeleteTransitionRouteGroup]. + + Attributes: + name (str): + Required. The name of the + [TransitionRouteGroup][google.cloud.dialogflow.cx.v3.TransitionRouteGroup] + to delete. Format: + ``projects//locations//agents//flows//transitionRouteGroups/``. + force (bool): + This field has no effect for transition route group that no + page is using. If the transition route group is referenced + by any page: + + - If ``force`` is set to false, an error will be returned + with message indicating pages that reference the + transition route group. + - If ``force`` is set to true, Dialogflow will remove the + transition route group, as well as any reference to it. + """ + + name = proto.Field(proto.STRING, number=1) + + force = proto.Field(proto.BOOL, number=2) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/version.py b/google/cloud/dialogflowcx_v3/types/version.py new file mode 100644 index 00000000..ebf6d440 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/version.py @@ -0,0 +1,242 @@ +# -*- 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.dialogflowcx_v3.types import flow +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "CreateVersionOperationMetadata", + "Version", + "ListVersionsRequest", + "ListVersionsResponse", + "GetVersionRequest", + "CreateVersionRequest", + "UpdateVersionRequest", + "DeleteVersionRequest", + "LoadVersionRequest", + }, +) + + +class CreateVersionOperationMetadata(proto.Message): + r"""Metadata associated with the long running operation for + [Versions.CreateVersion][google.cloud.dialogflow.cx.v3.Versions.CreateVersion]. + + Attributes: + version (str): + Name of the created version. Format: + ``projects//locations//agents//flows//versions/``. + """ + + version = proto.Field(proto.STRING, number=1) + + +class Version(proto.Message): + r"""Represents a version of a flow. + + Attributes: + name (str): + Format: projects//locations//agents//flows//versions/. + Version ID is a self-increasing number generated + by Dialogflow upon version creation. + display_name (str): + Required. The human-readable name of the + version. Limit of 64 characters. + description (str): + The description of the version. The maximum + length is 500 characters. If exceeded, the + request is rejected. + nlu_settings (~.flow.NluSettings): + Output only. The NLU settings of the flow at + version creation. + create_time (~.timestamp.Timestamp): + Output only. Create time of the version. + state (~.gcdc_version.Version.State): + Output only. The state of this version. This + field is read-only and cannot be set by create + and update methods. + """ + + class State(proto.Enum): + r"""The state of the version.""" + STATE_UNSPECIFIED = 0 + RUNNING = 1 + SUCCEEDED = 2 + FAILED = 3 + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + description = proto.Field(proto.STRING, number=3) + + nlu_settings = proto.Field(proto.MESSAGE, number=4, message=flow.NluSettings,) + + create_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) + + state = proto.Field(proto.ENUM, number=6, enum=State,) + + +class ListVersionsRequest(proto.Message): + r"""The request message for + [Versions.ListVersions][google.cloud.dialogflow.cx.v3.Versions.ListVersions]. + + Attributes: + parent (str): + Required. The [Flow][google.cloud.dialogflow.cx.v3.Flow] to + list all versions for. Format: + ``projects//locations//agents//flows/``. + page_size (int): + The maximum number of items to return in a + single page. By default 20 and at most 100. + page_token (str): + The next_page_token value returned from a previous list + request. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class ListVersionsResponse(proto.Message): + r"""The response message for + [Versions.ListVersions][google.cloud.dialogflow.cx.v3.Versions.ListVersions]. + + Attributes: + versions (Sequence[~.gcdc_version.Version]): + A list of versions. There will be a maximum number of items + returned based on the page_size field in the request. The + list may in some cases be empty or contain fewer entries + than page_size even if this isn't the last page. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + versions = proto.RepeatedField(proto.MESSAGE, number=1, message="Version",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetVersionRequest(proto.Message): + r"""The request message for + [Versions.GetVersion][google.cloud.dialogflow.cx.v3.Versions.GetVersion]. + + Attributes: + name (str): + Required. The name of the + [Version][google.cloud.dialogflow.cx.v3.Version]. Format: + ``projects//locations//agents//flows//versions/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreateVersionRequest(proto.Message): + r"""The request message for + [Versions.CreateVersion][google.cloud.dialogflow.cx.v3.Versions.CreateVersion]. + + Attributes: + parent (str): + Required. The [Flow][google.cloud.dialogflow.cx.v3.Flow] to + create an [Version][google.cloud.dialogflow.cx.v3.Version] + for. Format: + ``projects//locations//agents//flows/``. + version (~.gcdc_version.Version): + Required. The version to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + version = proto.Field(proto.MESSAGE, number=2, message="Version",) + + +class UpdateVersionRequest(proto.Message): + r"""The request message for + [Versions.UpdateVersion][google.cloud.dialogflow.cx.v3.Versions.UpdateVersion]. + + Attributes: + version (~.gcdc_version.Version): + Required. The version to update. + update_mask (~.field_mask.FieldMask): + Required. The mask to control which fields get updated. + Currently only ``description`` and ``display_name`` can be + updated. + """ + + version = proto.Field(proto.MESSAGE, number=1, message="Version",) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class DeleteVersionRequest(proto.Message): + r"""The request message for + [Versions.DeleteVersion][google.cloud.dialogflow.cx.v3.Versions.DeleteVersion]. + + Attributes: + name (str): + Required. The name of the + [Version][google.cloud.dialogflow.cx.v3.Version] to delete. + Format: + ``projects//locations//agents//flows//versions/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class LoadVersionRequest(proto.Message): + r"""The request message for + [Versions.LoadVersion][google.cloud.dialogflow.cx.v3.Versions.LoadVersion]. + + Attributes: + name (str): + Required. The + [Version][google.cloud.dialogflow.cx.v3.Version] to be + loaded to draft version. Format: + ``projects//locations//agents//flows//versions/``. + allow_override_agent_resources (bool): + This field is used to prevent accidental overwrite of other + agent resources in the draft version, which can potentially + impact other flow's behavior. If + ``allow_override_agent_resources`` is false, conflicted + agent-level resources will not be overridden (i.e. intents, + entities, webhooks). + """ + + name = proto.Field(proto.STRING, number=1) + + allow_override_agent_resources = proto.Field(proto.BOOL, number=2) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3/types/webhook.py b/google/cloud/dialogflowcx_v3/types/webhook.py new file mode 100644 index 00000000..e59ccca6 --- /dev/null +++ b/google/cloud/dialogflowcx_v3/types/webhook.py @@ -0,0 +1,544 @@ +# -*- 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.dialogflowcx_v3.types import response_message +from google.protobuf import duration_pb2 as duration # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.cx.v3", + manifest={ + "Webhook", + "ListWebhooksRequest", + "ListWebhooksResponse", + "GetWebhookRequest", + "CreateWebhookRequest", + "UpdateWebhookRequest", + "DeleteWebhookRequest", + "WebhookRequest", + "WebhookResponse", + "PageInfo", + "SessionInfo", + }, +) + + +class Webhook(proto.Message): + r"""Webhooks host the developer's business logic. During a + session, webhooks allow the developer to use the data extracted + by Dialogflow's natural language processing to generate dynamic + responses, validate collected data, or trigger actions on the + backend. + + Attributes: + name (str): + The unique identifier of the webhook. Required for the + [Webhooks.UpdateWebhook][google.cloud.dialogflow.cx.v3.Webhooks.UpdateWebhook] + method. + [Webhooks.CreateWebhook][google.cloud.dialogflow.cx.v3.Webhooks.CreateWebhook] + populates the name automatically. Format: + ``projects//locations//agents//webhooks/``. + display_name (str): + Required. The human-readable name of the + webhook, unique within the agent. + generic_web_service (~.gcdc_webhook.Webhook.GenericWebService): + Configuration for a generic web service. + timeout (~.duration.Duration): + Webhook execution timeout. Execution is + considered failed if Dialogflow doesn't receive + a response from webhook at the end of the + timeout period. Defaults to 5 seconds, maximum + allowed timeout is 30 seconds. + disabled (bool): + Indicates whether the webhook is disabled. + """ + + class GenericWebService(proto.Message): + r"""Represents configuration for a generic web service. + + Attributes: + uri (str): + Required. The webhook URI for receiving POST + requests. It must use https protocol. + username (str): + The user name for HTTP Basic authentication. + password (str): + The password for HTTP Basic authentication. + request_headers (Sequence[~.gcdc_webhook.Webhook.GenericWebService.RequestHeadersEntry]): + The HTTP request headers to send together + with webhook requests. + """ + + uri = proto.Field(proto.STRING, number=1) + + username = proto.Field(proto.STRING, number=2) + + password = proto.Field(proto.STRING, number=3) + + request_headers = proto.MapField(proto.STRING, proto.STRING, number=4) + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + generic_web_service = proto.Field( + proto.MESSAGE, number=4, oneof="webhook", message=GenericWebService, + ) + + timeout = proto.Field(proto.MESSAGE, number=6, message=duration.Duration,) + + disabled = proto.Field(proto.BOOL, number=5) + + +class ListWebhooksRequest(proto.Message): + r"""The request message for + [Webhooks.ListWebhooks][google.cloud.dialogflow.cx.v3.Webhooks.ListWebhooks]. + + Attributes: + parent (str): + Required. The agent to list all webhooks for. Format: + ``projects//locations//agents/``. + page_size (int): + The maximum number of items to return in a + single page. By default 100 and at most 1000. + page_token (str): + The next_page_token value returned from a previous list + request. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class ListWebhooksResponse(proto.Message): + r"""The response message for + [Webhooks.ListWebhooks][google.cloud.dialogflow.cx.v3.Webhooks.ListWebhooks]. + + Attributes: + webhooks (Sequence[~.gcdc_webhook.Webhook]): + The list of webhooks. There will be a maximum number of + items returned based on the page_size field in the request. + next_page_token (str): + Token to retrieve the next page of results, + or empty if there are no more results in the + list. + """ + + @property + def raw_page(self): + return self + + webhooks = proto.RepeatedField(proto.MESSAGE, number=1, message="Webhook",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetWebhookRequest(proto.Message): + r"""The request message for + [Webhooks.GetWebhook][google.cloud.dialogflow.cx.v3.Webhooks.GetWebhook]. + + Attributes: + name (str): + Required. The name of the webhook. Format: + ``projects//locations//agents//webhooks/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreateWebhookRequest(proto.Message): + r"""The request message for + [Webhooks.CreateWebhook][google.cloud.dialogflow.cx.v3.Webhooks.CreateWebhook]. + + Attributes: + parent (str): + Required. The agent to create a webhook for. Format: + ``projects//locations//agents/``. + webhook (~.gcdc_webhook.Webhook): + Required. The webhook to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + webhook = proto.Field(proto.MESSAGE, number=2, message="Webhook",) + + +class UpdateWebhookRequest(proto.Message): + r"""The request message for + [Webhooks.UpdateWebhook][google.cloud.dialogflow.cx.v3.Webhooks.UpdateWebhook]. + + Attributes: + webhook (~.gcdc_webhook.Webhook): + Required. The webhook to update. + update_mask (~.field_mask.FieldMask): + The mask to control which fields get updated. + If the mask is not present, all fields will be + updated. + """ + + webhook = proto.Field(proto.MESSAGE, number=1, message="Webhook",) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class DeleteWebhookRequest(proto.Message): + r"""The request message for + [Webhooks.DeleteWebhook][google.cloud.dialogflow.cx.v3.Webhooks.DeleteWebhook]. + + Attributes: + name (str): + Required. The name of the webhook to delete. Format: + ``projects//locations//agents//webhooks/``. + force (bool): + This field has no effect for webhook not being used. For + webhooks that are used by pages/flows/transition route + groups: + + - If ``force`` is set to false, an error will be returned + with message indicating the referenced resources. + - If ``force`` is set to true, Dialogflow will remove the + webhook, as well as any references to the webhook (i.e. + [Webhook][google.cloud.dialogflow.cx.v3.Fulfillment.webhook] + and + [tag][google.cloud.dialogflow.cx.v3.Fulfillment.tag]in + fulfillments that point to this webhook will be removed). + """ + + name = proto.Field(proto.STRING, number=1) + + force = proto.Field(proto.BOOL, number=2) + + +class WebhookRequest(proto.Message): + r"""The request message for a webhook call. + + Attributes: + detect_intent_response_id (str): + Always present. The unique identifier of the + [DetectIntentResponse][google.cloud.dialogflow.cx.v3.DetectIntentResponse] + that will be returned to the API caller. + fulfillment_info (~.gcdc_webhook.WebhookRequest.FulfillmentInfo): + Always present. Information about the + fulfillment that triggered this webhook call. + intent_info (~.gcdc_webhook.WebhookRequest.IntentInfo): + Information about the last matched intent. + page_info (~.gcdc_webhook.PageInfo): + Information about page status. + session_info (~.gcdc_webhook.SessionInfo): + Information about session status. + messages (Sequence[~.response_message.ResponseMessage]): + The list of rich message responses to present to the user. + Webhook can choose to append or replace this list in + [WebhookResponse.fulfillment_response][google.cloud.dialogflow.cx.v3.WebhookResponse.fulfillment_response]; + payload (~.struct.Struct): + Custom data set in + [QueryParameters.payload][google.cloud.dialogflow.cx.v3.QueryParameters.payload]. + """ + + class FulfillmentInfo(proto.Message): + r"""Represents fulfillment information communicated to the + webhook. + + Attributes: + tag (str): + Always present. The tag used to identify + which fulfillment is being called. + """ + + tag = proto.Field(proto.STRING, number=1) + + class IntentInfo(proto.Message): + r"""Represents intent information communicated to the webhook. + + Attributes: + last_matched_intent (str): + Always present. The unique identifier of the last matched + [intent][google.cloud.dialogflow.cx.v3.Intent]. Format: + ``projects//locations//agents//intents/``. + parameters (Sequence[~.gcdc_webhook.WebhookRequest.IntentInfo.ParametersEntry]): + Parameters identified as a result of intent + matching. This is a map of the name of the + identified parameter to the value of the + parameter identified from the user's utterance. + All parameters defined in the matched intent + that are identified will be surfaced here. + """ + + class IntentParameterValue(proto.Message): + r"""Represents a value for an intent parameter. + + Attributes: + original_value (str): + Always present. Original text value extracted + from user utterance. + resolved_value (~.struct.Value): + Always present. Structured value for the + parameter extracted from user utterance. + """ + + original_value = proto.Field(proto.STRING, number=1) + + resolved_value = proto.Field(proto.MESSAGE, number=2, message=struct.Value,) + + last_matched_intent = proto.Field(proto.STRING, number=1) + + parameters = proto.MapField( + proto.STRING, + proto.MESSAGE, + number=2, + message="WebhookRequest.IntentInfo.IntentParameterValue", + ) + + detect_intent_response_id = proto.Field(proto.STRING, number=1) + + fulfillment_info = proto.Field(proto.MESSAGE, number=6, message=FulfillmentInfo,) + + intent_info = proto.Field(proto.MESSAGE, number=3, message=IntentInfo,) + + page_info = proto.Field(proto.MESSAGE, number=4, message="PageInfo",) + + session_info = proto.Field(proto.MESSAGE, number=5, message="SessionInfo",) + + messages = proto.RepeatedField( + proto.MESSAGE, number=7, message=response_message.ResponseMessage, + ) + + payload = proto.Field(proto.MESSAGE, number=8, message=struct.Struct,) + + +class WebhookResponse(proto.Message): + r"""The response message for a webhook call. + + Attributes: + fulfillment_response (~.gcdc_webhook.WebhookResponse.FulfillmentResponse): + The fulfillment response to send to the user. + This field can be omitted by the webhook if it + does not intend to send any response to the + user. + page_info (~.gcdc_webhook.PageInfo): + Information about page status. This field can + be omitted by the webhook if it does not intend + to modify page status. + session_info (~.gcdc_webhook.SessionInfo): + Information about session status. This field + can be omitted by the webhook if it does not + intend to modify session status. + payload (~.struct.Struct): + Value to append directly to + [QueryResult.webhook_payloads][google.cloud.dialogflow.cx.v3.QueryResult.webhook_payloads]. + target_page (str): + The target page to transition to. Format: + ``projects//locations//agents//flows//pages/``. + target_flow (str): + The target flow to transition to. Format: + ``projects//locations//agents//flows/``. + """ + + class FulfillmentResponse(proto.Message): + r"""Represents a fulfillment response to the user. + + Attributes: + messages (Sequence[~.response_message.ResponseMessage]): + The list of rich message responses to present + to the user. + merge_behavior (~.gcdc_webhook.WebhookResponse.FulfillmentResponse.MergeBehavior): + Merge behavior for ``messages``. + """ + + class MergeBehavior(proto.Enum): + r"""Defines merge behavior for ``messages``.""" + MERGE_BEHAVIOR_UNSPECIFIED = 0 + APPEND = 1 + REPLACE = 2 + + messages = proto.RepeatedField( + proto.MESSAGE, number=1, message=response_message.ResponseMessage, + ) + + merge_behavior = proto.Field( + proto.ENUM, + number=2, + enum="WebhookResponse.FulfillmentResponse.MergeBehavior", + ) + + fulfillment_response = proto.Field( + proto.MESSAGE, number=1, message=FulfillmentResponse, + ) + + page_info = proto.Field(proto.MESSAGE, number=2, message="PageInfo",) + + session_info = proto.Field(proto.MESSAGE, number=3, message="SessionInfo",) + + payload = proto.Field(proto.MESSAGE, number=4, message=struct.Struct,) + + target_page = proto.Field(proto.STRING, number=5, oneof="transition") + + target_flow = proto.Field(proto.STRING, number=6, oneof="transition") + + +class PageInfo(proto.Message): + r"""Represents page information communicated to and from the + webhook. + + Attributes: + current_page (str): + Always present for + [WebhookRequest][google.cloud.dialogflow.cx.v3.WebhookRequest]. + Ignored for + [WebhookResponse][google.cloud.dialogflow.cx.v3.WebhookResponse]. + The unique identifier of the current page. Format: + ``projects//locations//agents//flows//pages/``. + form_info (~.gcdc_webhook.PageInfo.FormInfo): + Optional for both + [WebhookRequest][google.cloud.dialogflow.cx.v3.WebhookRequest] + and + [WebhookResponse][google.cloud.dialogflow.cx.v3.WebhookResponse]. + Information about the form. + """ + + class FormInfo(proto.Message): + r"""Represents form information. + + Attributes: + parameter_info (Sequence[~.gcdc_webhook.PageInfo.FormInfo.ParameterInfo]): + Optional for both + [WebhookRequest][google.cloud.dialogflow.cx.v3.WebhookRequest] + and + [WebhookResponse][google.cloud.dialogflow.cx.v3.WebhookResponse]. + The parameters contained in the form. Note that the webhook + cannot add or remove any form parameter. + """ + + class ParameterInfo(proto.Message): + r"""Represents parameter information. + + Attributes: + display_name (str): + Always present for + [WebhookRequest][google.cloud.dialogflow.cx.v3.WebhookRequest]. + Required for + [WebhookResponse][google.cloud.dialogflow.cx.v3.WebhookResponse]. + The human-readable name of the parameter, unique within the + form. This field cannot be modified by the webhook. + required (bool): + Optional for both + [WebhookRequest][google.cloud.dialogflow.cx.v3.WebhookRequest] + and + [WebhookResponse][google.cloud.dialogflow.cx.v3.WebhookResponse]. + Indicates whether the parameter is required. Optional + parameters will not trigger prompts; however, they are + filled if the user specifies them. Required parameters must + be filled before form filling concludes. + state (~.gcdc_webhook.PageInfo.FormInfo.ParameterInfo.ParameterState): + Always present for + [WebhookRequest][google.cloud.dialogflow.cx.v3.WebhookRequest]. + Required for + [WebhookResponse][google.cloud.dialogflow.cx.v3.WebhookResponse]. + The state of the parameter. This field can be set to + [INVALID][google.cloud.dialogflow.cx.v3.PageInfo.FormInfo.ParameterInfo.ParameterState.INVALID] + by the webhook to invalidate the parameter; other values set + by the webhook will be ignored. + value (~.struct.Value): + Optional for both + [WebhookRequest][google.cloud.dialogflow.cx.v3.WebhookRequest] + and + [WebhookResponse][google.cloud.dialogflow.cx.v3.WebhookResponse]. + The value of the parameter. This field can be set by the + webhook to change the parameter value. + just_collected (bool): + Optional for + [WebhookRequest][google.cloud.dialogflow.cx.v3.WebhookRequest]. + Ignored for + [WebhookResponse][google.cloud.dialogflow.cx.v3.WebhookResponse]. + Indicates if the parameter value was just collected on the + last conversation turn. + """ + + class ParameterState(proto.Enum): + r"""Represents the state of a parameter.""" + PARAMETER_STATE_UNSPECIFIED = 0 + EMPTY = 1 + INVALID = 2 + FILLED = 3 + + display_name = proto.Field(proto.STRING, number=1) + + required = proto.Field(proto.BOOL, number=2) + + state = proto.Field( + proto.ENUM, + number=3, + enum="PageInfo.FormInfo.ParameterInfo.ParameterState", + ) + + value = proto.Field(proto.MESSAGE, number=4, message=struct.Value,) + + just_collected = proto.Field(proto.BOOL, number=5) + + parameter_info = proto.RepeatedField( + proto.MESSAGE, number=2, message="PageInfo.FormInfo.ParameterInfo", + ) + + current_page = proto.Field(proto.STRING, number=1) + + form_info = proto.Field(proto.MESSAGE, number=3, message=FormInfo,) + + +class SessionInfo(proto.Message): + r"""Represents session information communicated to and from the + webhook. + + Attributes: + session (str): + Always present for + [WebhookRequest][google.cloud.dialogflow.cx.v3.WebhookRequest]. + Ignored for + [WebhookResponse][google.cloud.dialogflow.cx.v3.WebhookResponse]. + The unique identifier of the + [session][google.cloud.dialogflow.cx.v3.DetectIntentRequest.session]. + This field can be used by the webhook to identify a user. + Format: + ``projects//locations//agents//sessions/``. + parameters (Sequence[~.gcdc_webhook.SessionInfo.ParametersEntry]): + Optional for + [WebhookRequest][google.cloud.dialogflow.cx.v3.WebhookRequest]. + Optional for + [WebhookResponse][google.cloud.dialogflow.cx.v3.WebhookResponse]. + All parameters collected from forms and intents during the + session. Parameters can be created, updated, or removed by + the webhook. To remove a parameter from the session, the + webhook should explicitly set the parameter value to null in + [WebhookResponse][google.cloud.dialogflow.cx.v3.WebhookResponse]. + The map is keyed by parameters' display names. + """ + + session = proto.Field(proto.STRING, number=1) + + parameters = proto.MapField( + proto.STRING, proto.MESSAGE, number=2, message=struct.Value, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3beta1/__init__.py b/google/cloud/dialogflowcx_v3beta1/__init__.py index 5c1db577..693dda97 100644 --- a/google/cloud/dialogflowcx_v3beta1/__init__.py +++ b/google/cloud/dialogflowcx_v3beta1/__init__.py @@ -94,6 +94,8 @@ from .types.session import AudioInput from .types.session import DetectIntentRequest from .types.session import DetectIntentResponse +from .types.session import DtmfInput +from .types.session import EventInput from .types.session import FulfillIntentRequest from .types.session import FulfillIntentResponse from .types.session import IntentInput @@ -103,6 +105,7 @@ from .types.session import QueryInput from .types.session import QueryParameters from .types.session import QueryResult +from .types.session import SentimentAnalysisResult from .types.session import StreamingDetectIntentRequest from .types.session import StreamingDetectIntentResponse from .types.session import StreamingRecognitionResult @@ -171,11 +174,13 @@ "DeleteWebhookRequest", "DetectIntentRequest", "DetectIntentResponse", + "DtmfInput", "EntityType", "EntityTypesClient", "Environment", "EnvironmentsClient", "EventHandler", + "EventInput", "ExportAgentRequest", "ExportAgentResponse", "Flow", @@ -198,6 +203,7 @@ "Intent", "IntentInput", "IntentView", + "IntentsClient", "ListAgentsRequest", "ListAgentsResponse", "ListEntityTypesRequest", @@ -235,6 +241,7 @@ "QueryResult", "ResponseMessage", "RestoreAgentRequest", + "SentimentAnalysisResult", "SessionEntityType", "SessionEntityTypesClient", "SessionInfo", @@ -269,5 +276,4 @@ "WebhookRequest", "WebhookResponse", "WebhooksClient", - "IntentsClient", ) diff --git a/google/cloud/dialogflowcx_v3beta1/services/agents/async_client.py b/google/cloud/dialogflowcx_v3beta1/services/agents/async_client.py index c67427ea..30132710 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/agents/async_client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/agents/async_client.py @@ -28,8 +28,8 @@ from google.auth import credentials # type: ignore from google.oauth2 import service_account # type: ignore -from google.api_core import operation -from google.api_core import operation_async +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore from google.cloud.dialogflowcx_v3beta1.services.agents import pagers from google.cloud.dialogflowcx_v3beta1.types import agent from google.cloud.dialogflowcx_v3beta1.types import agent as gcdc_agent @@ -53,10 +53,41 @@ class AgentsAsyncClient: DEFAULT_MTLS_ENDPOINT = AgentsClient.DEFAULT_MTLS_ENDPOINT agent_path = staticmethod(AgentsClient.agent_path) + parse_agent_path = staticmethod(AgentsClient.parse_agent_path) + flow_path = staticmethod(AgentsClient.flow_path) + parse_flow_path = staticmethod(AgentsClient.parse_flow_path) + + common_billing_account_path = staticmethod(AgentsClient.common_billing_account_path) + parse_common_billing_account_path = staticmethod( + AgentsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(AgentsClient.common_folder_path) + parse_common_folder_path = staticmethod(AgentsClient.parse_common_folder_path) + + common_organization_path = staticmethod(AgentsClient.common_organization_path) + parse_common_organization_path = staticmethod( + AgentsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(AgentsClient.common_project_path) + parse_common_project_path = staticmethod(AgentsClient.parse_common_project_path) + + common_location_path = staticmethod(AgentsClient.common_location_path) + parse_common_location_path = staticmethod(AgentsClient.parse_common_location_path) from_service_account_file = AgentsClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> AgentsTransport: + """Return the transport used by the client instance. + + Returns: + AgentsTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(AgentsClient).get_transport_class, type(AgentsClient) ) @@ -83,16 +114,19 @@ def __init__( 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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + 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 @@ -147,7 +181,8 @@ async def list_agents( # 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. - if request is not None and any([parent]): + 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." @@ -236,7 +271,8 @@ async def get_agent( # 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. - if request is not None and any([name]): + 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." @@ -325,7 +361,8 @@ async def create_agent( # 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. - if request is not None and any([parent, agent]): + has_flattened_params = any([parent, agent]) + 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." @@ -417,7 +454,8 @@ async def update_agent( # 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. - if request is not None and any([agent, update_mask]): + has_flattened_params = any([agent, update_mask]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -486,7 +524,8 @@ async def delete_agent( # 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. - if request is not None and any([name]): + 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." @@ -527,7 +566,7 @@ async def export_agent( timeout: float = None, metadata: Sequence[Tuple[str, str]] = (), ) -> operation_async.AsyncOperation: - r"""Exports the specified agent to a ZIP file. + r"""Exports the specified agent to a binary file. Args: request (:class:`~.agent.ExportAgentRequest`): @@ -590,9 +629,10 @@ async def restore_agent( timeout: float = None, metadata: Sequence[Tuple[str, str]] = (), ) -> operation_async.AsyncOperation: - r"""Restores the specified agent from a ZIP file. - Note that all existing intents, intent routes, entity - types, pages and webhooks in the agent will be deleted. + r"""Restores the specified agent from a binary file. + Replaces the current agent with a new one. Note that all + existing resources in agent (e.g. intents, entity types, + flows) will be removed. Args: request (:class:`~.agent.RestoreAgentRequest`): diff --git a/google/cloud/dialogflowcx_v3beta1/services/agents/client.py b/google/cloud/dialogflowcx_v3beta1/services/agents/client.py index 258fba04..1a81f6a3 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/agents/client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/agents/client.py @@ -16,22 +16,24 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +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.api_core import operation -from google.api_core import operation_async +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore from google.cloud.dialogflowcx_v3beta1.services.agents import pagers from google.cloud.dialogflowcx_v3beta1.types import agent from google.cloud.dialogflowcx_v3beta1.types import agent as gcdc_agent @@ -134,6 +136,15 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> AgentsTransport: + """Return the transport used by the client instance. + + Returns: + AgentsTransport: The transport used by the client instance. + """ + return self._transport + @staticmethod def agent_path(project: str, location: str, agent: str,) -> str: """Return a fully-qualified agent string.""" @@ -150,12 +161,87 @@ def parse_agent_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def flow_path(project: str, location: str, agent: str, flow: str,) -> str: + """Return a fully-qualified flow string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + + @staticmethod + def parse_flow_path(path: str) -> Dict[str, str]: + """Parse a flow path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?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: credentials.Credentials = None, - transport: Union[str, AgentsTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, AgentsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the agents client. @@ -169,23 +255,26 @@ def __init__( transport (Union[str, ~.AgentsTransport]): 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. + client_options (client_options_lib.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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - 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 + 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: @@ -193,29 +282,47 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + 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")) + ) - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -239,10 +346,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + ssl_channel_credentials=ssl_credentials, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -678,7 +784,7 @@ def export_agent( timeout: float = None, metadata: Sequence[Tuple[str, str]] = (), ) -> operation.Operation: - r"""Exports the specified agent to a ZIP file. + r"""Exports the specified agent to a binary file. Args: request (:class:`~.agent.ExportAgentRequest`): @@ -742,9 +848,10 @@ def restore_agent( timeout: float = None, metadata: Sequence[Tuple[str, str]] = (), ) -> operation.Operation: - r"""Restores the specified agent from a ZIP file. - Note that all existing intents, intent routes, entity - types, pages and webhooks in the agent will be deleted. + r"""Restores the specified agent from a binary file. + Replaces the current agent with a new one. Note that all + existing resources in agent (e.g. intents, entity types, + flows) will be removed. Args: request (:class:`~.agent.RestoreAgentRequest`): diff --git a/google/cloud/dialogflowcx_v3beta1/services/agents/transports/grpc.py b/google/cloud/dialogflowcx_v3beta1/services/agents/transports/grpc.py index b793538c..45c15b55 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/agents/transports/grpc.py +++ b/google/cloud/dialogflowcx_v3beta1/services/agents/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -24,7 +25,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.dialogflowcx_v3beta1.types import agent @@ -61,6 +61,7 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -81,20 +82,22 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -103,6 +106,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -110,7 +115,13 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -141,6 +152,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) self._stubs = {} # type: Dict[str, Callable] @@ -201,19 +230,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.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 designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property @@ -365,7 +383,7 @@ def export_agent( ) -> Callable[[agent.ExportAgentRequest], operations.Operation]: r"""Return a callable for the export agent method over gRPC. - Exports the specified agent to a ZIP file. + Exports the specified agent to a binary file. Returns: Callable[[~.ExportAgentRequest], @@ -391,9 +409,10 @@ def restore_agent( ) -> Callable[[agent.RestoreAgentRequest], operations.Operation]: r"""Return a callable for the restore agent method over gRPC. - Restores the specified agent from a ZIP file. - Note that all existing intents, intent routes, entity - types, pages and webhooks in the agent will be deleted. + Restores the specified agent from a binary file. + Replaces the current agent with a new one. Note that all + existing resources in agent (e.g. intents, entity types, + flows) will be removed. Returns: Callable[[~.RestoreAgentRequest], diff --git a/google/cloud/dialogflowcx_v3beta1/services/agents/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3beta1/services/agents/transports/grpc_asyncio.py index 69916f83..1d38a3b4 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/agents/transports/grpc_asyncio.py +++ b/google/cloud/dialogflowcx_v3beta1/services/agents/transports/grpc_asyncio.py @@ -15,11 +15,13 @@ # 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.api_core import operations_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 @@ -103,6 +105,7 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -124,14 +127,16 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -146,6 +151,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -153,13 +160,24 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443" ) + 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: @@ -179,6 +197,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) # Run the base constructor. super().__init__( @@ -199,13 +235,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel @@ -360,7 +389,7 @@ def export_agent( ) -> Callable[[agent.ExportAgentRequest], Awaitable[operations.Operation]]: r"""Return a callable for the export agent method over gRPC. - Exports the specified agent to a ZIP file. + Exports the specified agent to a binary file. Returns: Callable[[~.ExportAgentRequest], @@ -386,9 +415,10 @@ def restore_agent( ) -> Callable[[agent.RestoreAgentRequest], Awaitable[operations.Operation]]: r"""Return a callable for the restore agent method over gRPC. - Restores the specified agent from a ZIP file. - Note that all existing intents, intent routes, entity - types, pages and webhooks in the agent will be deleted. + Restores the specified agent from a binary file. + Replaces the current agent with a new one. Note that all + existing resources in agent (e.g. intents, entity types, + flows) will be removed. Returns: Callable[[~.RestoreAgentRequest], diff --git a/google/cloud/dialogflowcx_v3beta1/services/entity_types/async_client.py b/google/cloud/dialogflowcx_v3beta1/services/entity_types/async_client.py index abdd9add..66e40946 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/entity_types/async_client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/entity_types/async_client.py @@ -49,10 +49,45 @@ class EntityTypesAsyncClient: DEFAULT_MTLS_ENDPOINT = EntityTypesClient.DEFAULT_MTLS_ENDPOINT entity_type_path = staticmethod(EntityTypesClient.entity_type_path) + parse_entity_type_path = staticmethod(EntityTypesClient.parse_entity_type_path) + + common_billing_account_path = staticmethod( + EntityTypesClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + EntityTypesClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(EntityTypesClient.common_folder_path) + parse_common_folder_path = staticmethod(EntityTypesClient.parse_common_folder_path) + + common_organization_path = staticmethod(EntityTypesClient.common_organization_path) + parse_common_organization_path = staticmethod( + EntityTypesClient.parse_common_organization_path + ) + + common_project_path = staticmethod(EntityTypesClient.common_project_path) + parse_common_project_path = staticmethod( + EntityTypesClient.parse_common_project_path + ) + + common_location_path = staticmethod(EntityTypesClient.common_location_path) + parse_common_location_path = staticmethod( + EntityTypesClient.parse_common_location_path + ) from_service_account_file = EntityTypesClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> EntityTypesTransport: + """Return the transport used by the client instance. + + Returns: + EntityTypesTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(EntityTypesClient).get_transport_class, type(EntityTypesClient) ) @@ -79,16 +114,19 @@ def __init__( 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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + 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 @@ -144,7 +182,8 @@ async def list_entity_types( # 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. - if request is not None and any([parent]): + 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." @@ -254,7 +293,8 @@ async def get_entity_type( # 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. - if request is not None and any([name]): + 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." @@ -364,7 +404,8 @@ async def create_entity_type( # 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. - if request is not None and any([parent, entity_type]): + has_flattened_params = any([parent, entity_type]) + 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." @@ -476,7 +517,8 @@ async def update_entity_type( # 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. - if request is not None and any([entity_type, update_mask]): + has_flattened_params = any([entity_type, update_mask]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -545,7 +587,8 @@ async def delete_entity_type( # 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. - if request is not None and any([name]): + 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." diff --git a/google/cloud/dialogflowcx_v3beta1/services/entity_types/client.py b/google/cloud/dialogflowcx_v3beta1/services/entity_types/client.py index d8e3a513..6956b1f5 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/entity_types/client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/entity_types/client.py @@ -16,17 +16,19 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +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 @@ -130,6 +132,15 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> EntityTypesTransport: + """Return the transport used by the client instance. + + Returns: + EntityTypesTransport: The transport used by the client instance. + """ + return self._transport + @staticmethod def entity_type_path( project: str, location: str, agent: str, entity_type: str, @@ -148,12 +159,71 @@ def parse_entity_type_path(path: str) -> Dict[str, str]: ) 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: credentials.Credentials = None, - transport: Union[str, EntityTypesTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, EntityTypesTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the entity types client. @@ -167,23 +237,26 @@ def __init__( transport (Union[str, ~.EntityTypesTransport]): 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. + client_options (client_options_lib.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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - 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 + 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: @@ -191,29 +264,47 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + 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")) + ) - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -237,10 +328,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + ssl_channel_credentials=ssl_credentials, quota_project_id=client_options.quota_project_id, client_info=client_info, ) diff --git a/google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/grpc.py b/google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/grpc.py index e86a60d1..54d83c89 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/grpc.py +++ b/google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -23,7 +24,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.dialogflowcx_v3beta1.types import entity_type @@ -59,6 +59,7 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -79,20 +80,22 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -101,6 +104,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -108,7 +113,13 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -139,6 +150,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) self._stubs = {} # type: Dict[str, Callable] @@ -199,19 +228,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.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 designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/grpc_asyncio.py index 9b50722f..4ee2bdd2 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/grpc_asyncio.py +++ b/google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/grpc_asyncio.py @@ -15,10 +15,12 @@ # 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 @@ -101,6 +103,7 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -122,14 +125,16 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -144,6 +149,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -151,13 +158,24 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443" ) + 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: @@ -177,6 +195,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) # Run the base constructor. super().__init__( @@ -197,13 +233,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/dialogflowcx_v3beta1/services/environments/async_client.py b/google/cloud/dialogflowcx_v3beta1/services/environments/async_client.py index 6f84dda8..38c4a0a3 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/environments/async_client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/environments/async_client.py @@ -28,8 +28,8 @@ from google.auth import credentials # type: ignore from google.oauth2 import service_account # type: ignore -from google.api_core import operation -from google.api_core import operation_async +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore from google.cloud.dialogflowcx_v3beta1.services.environments import pagers from google.cloud.dialogflowcx_v3beta1.types import environment from google.cloud.dialogflowcx_v3beta1.types import environment as gcdc_environment @@ -53,10 +53,47 @@ class EnvironmentsAsyncClient: DEFAULT_MTLS_ENDPOINT = EnvironmentsClient.DEFAULT_MTLS_ENDPOINT environment_path = staticmethod(EnvironmentsClient.environment_path) + parse_environment_path = staticmethod(EnvironmentsClient.parse_environment_path) + version_path = staticmethod(EnvironmentsClient.version_path) + parse_version_path = staticmethod(EnvironmentsClient.parse_version_path) + + common_billing_account_path = staticmethod( + EnvironmentsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + EnvironmentsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(EnvironmentsClient.common_folder_path) + parse_common_folder_path = staticmethod(EnvironmentsClient.parse_common_folder_path) + + common_organization_path = staticmethod(EnvironmentsClient.common_organization_path) + parse_common_organization_path = staticmethod( + EnvironmentsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(EnvironmentsClient.common_project_path) + parse_common_project_path = staticmethod( + EnvironmentsClient.parse_common_project_path + ) + + common_location_path = staticmethod(EnvironmentsClient.common_location_path) + parse_common_location_path = staticmethod( + EnvironmentsClient.parse_common_location_path + ) from_service_account_file = EnvironmentsClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> EnvironmentsTransport: + """Return the transport used by the client instance. + + Returns: + EnvironmentsTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(EnvironmentsClient).get_transport_class, type(EnvironmentsClient) ) @@ -83,16 +120,19 @@ def __init__( 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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + 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 @@ -149,7 +189,8 @@ async def list_environments( # 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. - if request is not None and any([parent]): + 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." @@ -242,7 +283,8 @@ async def get_environment( # 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. - if request is not None and any([name]): + 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." @@ -337,7 +379,8 @@ async def create_environment( # 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. - if request is not None and any([parent, environment]): + has_flattened_params = any([parent, environment]) + 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." @@ -437,7 +480,8 @@ async def update_environment( # 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. - if request is not None and any([environment, update_mask]): + has_flattened_params = any([environment, update_mask]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -517,7 +561,8 @@ async def delete_environment( # 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. - if request is not None and any([name]): + 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." @@ -592,7 +637,8 @@ async def lookup_environment_history( # 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. - if request is not None and any([name]): + 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." diff --git a/google/cloud/dialogflowcx_v3beta1/services/environments/client.py b/google/cloud/dialogflowcx_v3beta1/services/environments/client.py index 5ca1922a..03461fed 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/environments/client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/environments/client.py @@ -16,22 +16,24 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +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.api_core import operation -from google.api_core import operation_async +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore from google.cloud.dialogflowcx_v3beta1.services.environments import pagers from google.cloud.dialogflowcx_v3beta1.types import environment from google.cloud.dialogflowcx_v3beta1.types import environment as gcdc_environment @@ -134,6 +136,15 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> EnvironmentsTransport: + """Return the transport used by the client instance. + + Returns: + EnvironmentsTransport: The transport used by the client instance. + """ + return self._transport + @staticmethod def environment_path( project: str, location: str, agent: str, environment: str, @@ -152,12 +163,89 @@ def parse_environment_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def version_path( + project: str, location: str, agent: str, flow: str, version: str, + ) -> str: + """Return a fully-qualified version string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/versions/{version}".format( + project=project, location=location, agent=agent, flow=flow, version=version, + ) + + @staticmethod + def parse_version_path(path: str) -> Dict[str, str]: + """Parse a version path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/versions/(?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: credentials.Credentials = None, - transport: Union[str, EnvironmentsTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, EnvironmentsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the environments client. @@ -171,23 +259,26 @@ def __init__( transport (Union[str, ~.EnvironmentsTransport]): 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. + client_options (client_options_lib.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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - 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 + 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: @@ -195,29 +286,47 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + 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")) + ) - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -241,10 +350,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + ssl_channel_credentials=ssl_credentials, quota_project_id=client_options.quota_project_id, client_info=client_info, ) diff --git a/google/cloud/dialogflowcx_v3beta1/services/environments/transports/grpc.py b/google/cloud/dialogflowcx_v3beta1/services/environments/transports/grpc.py index cd7d6ced..3d43e216 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/environments/transports/grpc.py +++ b/google/cloud/dialogflowcx_v3beta1/services/environments/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -24,7 +25,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.dialogflowcx_v3beta1.types import environment @@ -61,6 +61,7 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -81,20 +82,22 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -103,6 +106,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -110,7 +115,13 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -141,6 +152,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) self._stubs = {} # type: Dict[str, Callable] @@ -201,19 +230,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.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 designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/dialogflowcx_v3beta1/services/environments/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3beta1/services/environments/transports/grpc_asyncio.py index 421fba5b..8292443a 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/environments/transports/grpc_asyncio.py +++ b/google/cloud/dialogflowcx_v3beta1/services/environments/transports/grpc_asyncio.py @@ -15,11 +15,13 @@ # 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.api_core import operations_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 @@ -103,6 +105,7 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -124,14 +127,16 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -146,6 +151,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -153,13 +160,24 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443" ) + 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: @@ -179,6 +197,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) # Run the base constructor. super().__init__( @@ -199,13 +235,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/dialogflowcx_v3beta1/services/flows/async_client.py b/google/cloud/dialogflowcx_v3beta1/services/flows/async_client.py index 48a776db..06951967 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/flows/async_client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/flows/async_client.py @@ -28,8 +28,8 @@ from google.auth import credentials # type: ignore from google.oauth2 import service_account # type: ignore -from google.api_core import operation -from google.api_core import operation_async +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore from google.cloud.dialogflowcx_v3beta1.services.flows import pagers from google.cloud.dialogflowcx_v3beta1.types import flow from google.cloud.dialogflowcx_v3beta1.types import flow as gcdc_flow @@ -54,10 +54,45 @@ class FlowsAsyncClient: DEFAULT_MTLS_ENDPOINT = FlowsClient.DEFAULT_MTLS_ENDPOINT flow_path = staticmethod(FlowsClient.flow_path) + parse_flow_path = staticmethod(FlowsClient.parse_flow_path) + intent_path = staticmethod(FlowsClient.intent_path) + parse_intent_path = staticmethod(FlowsClient.parse_intent_path) + page_path = staticmethod(FlowsClient.page_path) + parse_page_path = staticmethod(FlowsClient.parse_page_path) + webhook_path = staticmethod(FlowsClient.webhook_path) + parse_webhook_path = staticmethod(FlowsClient.parse_webhook_path) + + common_billing_account_path = staticmethod(FlowsClient.common_billing_account_path) + parse_common_billing_account_path = staticmethod( + FlowsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(FlowsClient.common_folder_path) + parse_common_folder_path = staticmethod(FlowsClient.parse_common_folder_path) + + common_organization_path = staticmethod(FlowsClient.common_organization_path) + parse_common_organization_path = staticmethod( + FlowsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(FlowsClient.common_project_path) + parse_common_project_path = staticmethod(FlowsClient.parse_common_project_path) + + common_location_path = staticmethod(FlowsClient.common_location_path) + parse_common_location_path = staticmethod(FlowsClient.parse_common_location_path) from_service_account_file = FlowsClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> FlowsTransport: + """Return the transport used by the client instance. + + Returns: + FlowsTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(FlowsClient).get_transport_class, type(FlowsClient) ) @@ -84,16 +119,19 @@ def __init__( 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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + 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 @@ -171,7 +209,8 @@ async def create_flow( # 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. - if request is not None and any([parent, flow]): + has_flattened_params = any([parent, flow]) + 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." @@ -238,7 +277,8 @@ async def delete_flow( # 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. - if request is not None and any([name]): + 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." @@ -311,7 +351,8 @@ async def list_flows( # 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. - if request is not None and any([parent]): + 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." @@ -409,7 +450,8 @@ async def get_flow( # 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. - if request is not None and any([name]): + 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." @@ -508,7 +550,8 @@ async def update_flow( # 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. - if request is not None and any([flow, update_mask]): + has_flattened_params = any([flow, update_mask]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -599,7 +642,8 @@ async def train_flow( # 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. - if request is not None and any([name]): + 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." diff --git a/google/cloud/dialogflowcx_v3beta1/services/flows/client.py b/google/cloud/dialogflowcx_v3beta1/services/flows/client.py index b1592cca..0cd50c1a 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/flows/client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/flows/client.py @@ -16,22 +16,24 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +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.api_core import operation -from google.api_core import operation_async +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore from google.cloud.dialogflowcx_v3beta1.services.flows import pagers from google.cloud.dialogflowcx_v3beta1.types import flow from google.cloud.dialogflowcx_v3beta1.types import flow as gcdc_flow @@ -135,6 +137,15 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> FlowsTransport: + """Return the transport used by the client instance. + + Returns: + FlowsTransport: The transport used by the client instance. + """ + return self._transport + @staticmethod def flow_path(project: str, location: str, agent: str, flow: str,) -> str: """Return a fully-qualified flow string.""" @@ -151,12 +162,121 @@ def parse_flow_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def intent_path(project: str, location: str, agent: str, intent: str,) -> str: + """Return a fully-qualified intent string.""" + return "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + + @staticmethod + def parse_intent_path(path: str) -> Dict[str, str]: + """Parse a intent path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/intents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def page_path( + project: str, location: str, agent: str, flow: str, page: str, + ) -> str: + """Return a fully-qualified page string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + + @staticmethod + def parse_page_path(path: str) -> Dict[str, str]: + """Parse a page path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/pages/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def webhook_path(project: str, location: str, agent: str, webhook: str,) -> str: + """Return a fully-qualified webhook string.""" + return "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + + @staticmethod + def parse_webhook_path(path: str) -> Dict[str, str]: + """Parse a webhook path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/webhooks/(?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: credentials.Credentials = None, - transport: Union[str, FlowsTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, FlowsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the flows client. @@ -170,23 +290,26 @@ def __init__( transport (Union[str, ~.FlowsTransport]): 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. + client_options (client_options_lib.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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - 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 + 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: @@ -194,29 +317,47 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + client_options = client_options_lib.ClientOptions() - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -240,10 +381,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + ssl_channel_credentials=ssl_credentials, quota_project_id=client_options.quota_project_id, client_info=client_info, ) diff --git a/google/cloud/dialogflowcx_v3beta1/services/flows/transports/grpc.py b/google/cloud/dialogflowcx_v3beta1/services/flows/transports/grpc.py index fbca5886..0def5e99 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/flows/transports/grpc.py +++ b/google/cloud/dialogflowcx_v3beta1/services/flows/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -24,7 +25,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.dialogflowcx_v3beta1.types import flow @@ -61,6 +61,7 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -81,20 +82,22 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -103,6 +106,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -110,7 +115,13 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -141,6 +152,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) self._stubs = {} # type: Dict[str, Callable] @@ -201,19 +230,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.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 designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/dialogflowcx_v3beta1/services/flows/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3beta1/services/flows/transports/grpc_asyncio.py index 1bebe48d..fec5fce5 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/flows/transports/grpc_asyncio.py +++ b/google/cloud/dialogflowcx_v3beta1/services/flows/transports/grpc_asyncio.py @@ -15,11 +15,13 @@ # 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.api_core import operations_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 @@ -103,6 +105,7 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -124,14 +127,16 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -146,6 +151,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -153,13 +160,24 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443" ) + 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: @@ -179,6 +197,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) # Run the base constructor. super().__init__( @@ -199,13 +235,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/dialogflowcx_v3beta1/services/intents/async_client.py b/google/cloud/dialogflowcx_v3beta1/services/intents/async_client.py index 71645143..e0082d6b 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/intents/async_client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/intents/async_client.py @@ -48,11 +48,44 @@ class IntentsAsyncClient: DEFAULT_ENDPOINT = IntentsClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = IntentsClient.DEFAULT_MTLS_ENDPOINT + entity_type_path = staticmethod(IntentsClient.entity_type_path) + parse_entity_type_path = staticmethod(IntentsClient.parse_entity_type_path) intent_path = staticmethod(IntentsClient.intent_path) + parse_intent_path = staticmethod(IntentsClient.parse_intent_path) + + common_billing_account_path = staticmethod( + IntentsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + IntentsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(IntentsClient.common_folder_path) + parse_common_folder_path = staticmethod(IntentsClient.parse_common_folder_path) + + common_organization_path = staticmethod(IntentsClient.common_organization_path) + parse_common_organization_path = staticmethod( + IntentsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(IntentsClient.common_project_path) + parse_common_project_path = staticmethod(IntentsClient.parse_common_project_path) + + common_location_path = staticmethod(IntentsClient.common_location_path) + parse_common_location_path = staticmethod(IntentsClient.parse_common_location_path) from_service_account_file = IntentsClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> IntentsTransport: + """Return the transport used by the client instance. + + Returns: + IntentsTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(IntentsClient).get_transport_class, type(IntentsClient) ) @@ -79,16 +112,19 @@ def __init__( 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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + 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 @@ -143,7 +179,8 @@ async def list_intents( # 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. - if request is not None and any([parent]): + 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." @@ -225,7 +262,8 @@ async def get_intent( # 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. - if request is not None and any([name]): + 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." @@ -307,7 +345,8 @@ async def create_intent( # 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. - if request is not None and any([parent, intent]): + has_flattened_params = any([parent, intent]) + 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." @@ -392,7 +431,8 @@ async def update_intent( # 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. - if request is not None and any([intent, update_mask]): + has_flattened_params = any([intent, update_mask]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -461,7 +501,8 @@ async def delete_intent( # 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. - if request is not None and any([name]): + 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." diff --git a/google/cloud/dialogflowcx_v3beta1/services/intents/client.py b/google/cloud/dialogflowcx_v3beta1/services/intents/client.py index 28ccecd9..44250d8e 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/intents/client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/intents/client.py @@ -16,17 +16,19 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +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 @@ -130,6 +132,33 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> IntentsTransport: + """Return the transport used by the client instance. + + Returns: + IntentsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def entity_type_path( + project: str, location: str, agent: str, entity_type: str, + ) -> str: + """Return a fully-qualified entity_type string.""" + return "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + + @staticmethod + def parse_entity_type_path(path: str) -> Dict[str, str]: + """Parse a entity_type path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/entityTypes/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + @staticmethod def intent_path(project: str, location: str, agent: str, intent: str,) -> str: """Return a fully-qualified intent string.""" @@ -146,12 +175,71 @@ def parse_intent_path(path: str) -> Dict[str, str]: ) 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: credentials.Credentials = None, - transport: Union[str, IntentsTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, IntentsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the intents client. @@ -165,23 +253,26 @@ def __init__( transport (Union[str, ~.IntentsTransport]): 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. + client_options (client_options_lib.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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - 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 + 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: @@ -189,29 +280,47 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + 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")) + ) - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -235,10 +344,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + ssl_channel_credentials=ssl_credentials, quota_project_id=client_options.quota_project_id, client_info=client_info, ) diff --git a/google/cloud/dialogflowcx_v3beta1/services/intents/transports/grpc.py b/google/cloud/dialogflowcx_v3beta1/services/intents/transports/grpc.py index a0d878d5..0c52b8b4 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/intents/transports/grpc.py +++ b/google/cloud/dialogflowcx_v3beta1/services/intents/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -23,7 +24,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.dialogflowcx_v3beta1.types import intent @@ -59,6 +59,7 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -79,20 +80,22 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -101,6 +104,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -108,7 +113,13 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -139,6 +150,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) self._stubs = {} # type: Dict[str, Callable] @@ -199,19 +228,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.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 designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/dialogflowcx_v3beta1/services/intents/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3beta1/services/intents/transports/grpc_asyncio.py index 3072135a..b441089d 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/intents/transports/grpc_asyncio.py +++ b/google/cloud/dialogflowcx_v3beta1/services/intents/transports/grpc_asyncio.py @@ -15,10 +15,12 @@ # 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 @@ -101,6 +103,7 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -122,14 +125,16 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -144,6 +149,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -151,13 +158,24 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443" ) + 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: @@ -177,6 +195,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) # Run the base constructor. super().__init__( @@ -197,13 +233,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/dialogflowcx_v3beta1/services/pages/async_client.py b/google/cloud/dialogflowcx_v3beta1/services/pages/async_client.py index 59767c46..b16d077b 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/pages/async_client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/pages/async_client.py @@ -49,11 +49,52 @@ class PagesAsyncClient: DEFAULT_ENDPOINT = PagesClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = PagesClient.DEFAULT_MTLS_ENDPOINT + entity_type_path = staticmethod(PagesClient.entity_type_path) + parse_entity_type_path = staticmethod(PagesClient.parse_entity_type_path) + flow_path = staticmethod(PagesClient.flow_path) + parse_flow_path = staticmethod(PagesClient.parse_flow_path) + intent_path = staticmethod(PagesClient.intent_path) + parse_intent_path = staticmethod(PagesClient.parse_intent_path) page_path = staticmethod(PagesClient.page_path) + parse_page_path = staticmethod(PagesClient.parse_page_path) + transition_route_group_path = staticmethod(PagesClient.transition_route_group_path) + parse_transition_route_group_path = staticmethod( + PagesClient.parse_transition_route_group_path + ) + webhook_path = staticmethod(PagesClient.webhook_path) + parse_webhook_path = staticmethod(PagesClient.parse_webhook_path) + + common_billing_account_path = staticmethod(PagesClient.common_billing_account_path) + parse_common_billing_account_path = staticmethod( + PagesClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(PagesClient.common_folder_path) + parse_common_folder_path = staticmethod(PagesClient.parse_common_folder_path) + + common_organization_path = staticmethod(PagesClient.common_organization_path) + parse_common_organization_path = staticmethod( + PagesClient.parse_common_organization_path + ) + + common_project_path = staticmethod(PagesClient.common_project_path) + parse_common_project_path = staticmethod(PagesClient.parse_common_project_path) + + common_location_path = staticmethod(PagesClient.common_location_path) + parse_common_location_path = staticmethod(PagesClient.parse_common_location_path) from_service_account_file = PagesClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> PagesTransport: + """Return the transport used by the client instance. + + Returns: + PagesTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(PagesClient).get_transport_class, type(PagesClient) ) @@ -80,16 +121,19 @@ def __init__( 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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + 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 @@ -143,7 +187,8 @@ async def list_pages( # 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. - if request is not None and any([parent]): + 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." @@ -239,7 +284,8 @@ async def get_page( # 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. - if request is not None and any([name]): + 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." @@ -335,7 +381,8 @@ async def create_page( # 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. - if request is not None and any([parent, page]): + has_flattened_params = any([parent, page]) + 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." @@ -434,7 +481,8 @@ async def update_page( # 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. - if request is not None and any([page, update_mask]): + has_flattened_params = any([page, update_mask]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -503,7 +551,8 @@ async def delete_page( # 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. - if request is not None and any([name]): + 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." diff --git a/google/cloud/dialogflowcx_v3beta1/services/pages/client.py b/google/cloud/dialogflowcx_v3beta1/services/pages/client.py index 26e40e83..011f3033 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/pages/client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/pages/client.py @@ -16,17 +16,19 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +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 @@ -131,6 +133,65 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> PagesTransport: + """Return the transport used by the client instance. + + Returns: + PagesTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def entity_type_path( + project: str, location: str, agent: str, entity_type: str, + ) -> str: + """Return a fully-qualified entity_type string.""" + return "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + + @staticmethod + def parse_entity_type_path(path: str) -> Dict[str, str]: + """Parse a entity_type path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/entityTypes/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def flow_path(project: str, location: str, agent: str, flow: str,) -> str: + """Return a fully-qualified flow string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + + @staticmethod + def parse_flow_path(path: str) -> Dict[str, str]: + """Parse a flow path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def intent_path(project: str, location: str, agent: str, intent: str,) -> str: + """Return a fully-qualified intent string.""" + return "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + + @staticmethod + def parse_intent_path(path: str) -> Dict[str, str]: + """Parse a intent path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/intents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + @staticmethod def page_path( project: str, location: str, agent: str, flow: str, page: str, @@ -149,12 +210,109 @@ def parse_page_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def transition_route_group_path( + project: str, location: str, agent: str, flow: str, transition_route_group: str, + ) -> str: + """Return a fully-qualified transition_route_group string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/transitionRouteGroups/{transition_route_group}".format( + project=project, + location=location, + agent=agent, + flow=flow, + transition_route_group=transition_route_group, + ) + + @staticmethod + def parse_transition_route_group_path(path: str) -> Dict[str, str]: + """Parse a transition_route_group path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/transitionRouteGroups/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def webhook_path(project: str, location: str, agent: str, webhook: str,) -> str: + """Return a fully-qualified webhook string.""" + return "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + + @staticmethod + def parse_webhook_path(path: str) -> Dict[str, str]: + """Parse a webhook path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/webhooks/(?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: credentials.Credentials = None, - transport: Union[str, PagesTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, PagesTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the pages client. @@ -168,23 +326,26 @@ def __init__( transport (Union[str, ~.PagesTransport]): 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. + client_options (client_options_lib.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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - 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 + 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: @@ -192,29 +353,47 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + 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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -238,10 +417,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + ssl_channel_credentials=ssl_credentials, quota_project_id=client_options.quota_project_id, client_info=client_info, ) diff --git a/google/cloud/dialogflowcx_v3beta1/services/pages/transports/grpc.py b/google/cloud/dialogflowcx_v3beta1/services/pages/transports/grpc.py index 9e05e761..1f68894e 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/pages/transports/grpc.py +++ b/google/cloud/dialogflowcx_v3beta1/services/pages/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -23,7 +24,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.dialogflowcx_v3beta1.types import page @@ -59,6 +59,7 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -79,20 +80,22 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -101,6 +104,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -108,7 +113,13 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -139,6 +150,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) self._stubs = {} # type: Dict[str, Callable] @@ -199,19 +228,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.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 designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/dialogflowcx_v3beta1/services/pages/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3beta1/services/pages/transports/grpc_asyncio.py index 14b5c543..a9fe2225 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/pages/transports/grpc_asyncio.py +++ b/google/cloud/dialogflowcx_v3beta1/services/pages/transports/grpc_asyncio.py @@ -15,10 +15,12 @@ # 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 @@ -101,6 +103,7 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -122,14 +125,16 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -144,6 +149,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -151,13 +158,24 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443" ) + 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: @@ -177,6 +195,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) # Run the base constructor. super().__init__( @@ -197,13 +233,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/async_client.py b/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/async_client.py index b6a261ab..bcde3f15 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/async_client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/async_client.py @@ -54,10 +54,51 @@ class SessionEntityTypesAsyncClient: session_entity_type_path = staticmethod( SessionEntityTypesClient.session_entity_type_path ) + parse_session_entity_type_path = staticmethod( + SessionEntityTypesClient.parse_session_entity_type_path + ) + + common_billing_account_path = staticmethod( + SessionEntityTypesClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + SessionEntityTypesClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(SessionEntityTypesClient.common_folder_path) + parse_common_folder_path = staticmethod( + SessionEntityTypesClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + SessionEntityTypesClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + SessionEntityTypesClient.parse_common_organization_path + ) + + common_project_path = staticmethod(SessionEntityTypesClient.common_project_path) + parse_common_project_path = staticmethod( + SessionEntityTypesClient.parse_common_project_path + ) + + common_location_path = staticmethod(SessionEntityTypesClient.common_location_path) + parse_common_location_path = staticmethod( + SessionEntityTypesClient.parse_common_location_path + ) from_service_account_file = SessionEntityTypesClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> SessionEntityTypesTransport: + """Return the transport used by the client instance. + + Returns: + SessionEntityTypesTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(SessionEntityTypesClient).get_transport_class, type(SessionEntityTypesClient), @@ -85,16 +126,19 @@ def __init__( 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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + 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 @@ -154,7 +198,8 @@ async def list_session_entity_types( # 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. - if request is not None and any([parent]): + 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." @@ -250,7 +295,8 @@ async def get_session_entity_type( # 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. - if request is not None and any([name]): + 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." @@ -350,7 +396,8 @@ async def create_session_entity_type( # 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. - if request is not None and any([parent, session_entity_type]): + has_flattened_params = any([parent, session_entity_type]) + 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." @@ -449,7 +496,8 @@ async def update_session_entity_type( # 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. - if request is not None and any([session_entity_type, update_mask]): + has_flattened_params = any([session_entity_type, update_mask]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -523,7 +571,8 @@ async def delete_session_entity_type( # 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. - if request is not None and any([name]): + 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." diff --git a/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/client.py b/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/client.py index dde78c9a..3fe6e56d 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/client.py @@ -16,17 +16,19 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +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 @@ -137,6 +139,15 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> SessionEntityTypesTransport: + """Return the transport used by the client instance. + + Returns: + SessionEntityTypesTransport: The transport used by the client instance. + """ + return self._transport + @staticmethod def session_entity_type_path( project: str, location: str, agent: str, session: str, entity_type: str, @@ -159,12 +170,71 @@ def parse_session_entity_type_path(path: str) -> Dict[str, str]: ) 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: credentials.Credentials = None, - transport: Union[str, SessionEntityTypesTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, SessionEntityTypesTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the session entity types client. @@ -178,23 +248,26 @@ def __init__( transport (Union[str, ~.SessionEntityTypesTransport]): 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. + client_options (client_options_lib.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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - 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 + 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: @@ -202,29 +275,47 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + 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")) + ) - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -248,10 +339,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + ssl_channel_credentials=ssl_credentials, quota_project_id=client_options.quota_project_id, client_info=client_info, ) diff --git a/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/grpc.py b/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/grpc.py index 5a39febd..1f3d4c2f 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/grpc.py +++ b/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -23,7 +24,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.dialogflowcx_v3beta1.types import session_entity_type @@ -61,6 +61,7 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -81,20 +82,22 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -103,6 +106,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -110,7 +115,13 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -141,6 +152,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) self._stubs = {} # type: Dict[str, Callable] @@ -201,19 +230,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.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 designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/grpc_asyncio.py index 31775b9f..1cc6e983 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/grpc_asyncio.py +++ b/google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/grpc_asyncio.py @@ -15,10 +15,12 @@ # 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 @@ -103,6 +105,7 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -124,14 +127,16 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -146,6 +151,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -153,13 +160,24 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443" ) + 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: @@ -179,6 +197,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) # Run the base constructor. super().__init__( @@ -199,13 +235,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/dialogflowcx_v3beta1/services/sessions/async_client.py b/google/cloud/dialogflowcx_v3beta1/services/sessions/async_client.py index 11950c06..b4993bfb 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/sessions/async_client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/sessions/async_client.py @@ -18,7 +18,16 @@ from collections import OrderedDict import functools import re -from typing import Dict, AsyncIterable, AsyncIterator, Sequence, Tuple, Type, Union +from typing import ( + Dict, + AsyncIterable, + Awaitable, + AsyncIterator, + Sequence, + Tuple, + Type, + Union, +) import pkg_resources import google.api_core.client_options as ClientOptions # type: ignore @@ -49,13 +58,62 @@ class SessionsAsyncClient: DEFAULT_ENDPOINT = SessionsClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = SessionsClient.DEFAULT_MTLS_ENDPOINT + entity_type_path = staticmethod(SessionsClient.entity_type_path) + parse_entity_type_path = staticmethod(SessionsClient.parse_entity_type_path) + flow_path = staticmethod(SessionsClient.flow_path) + parse_flow_path = staticmethod(SessionsClient.parse_flow_path) + intent_path = staticmethod(SessionsClient.intent_path) + parse_intent_path = staticmethod(SessionsClient.parse_intent_path) + page_path = staticmethod(SessionsClient.page_path) + parse_page_path = staticmethod(SessionsClient.parse_page_path) + session_path = staticmethod(SessionsClient.session_path) + parse_session_path = staticmethod(SessionsClient.parse_session_path) session_entity_type_path = staticmethod(SessionsClient.session_entity_type_path) + parse_session_entity_type_path = staticmethod( + SessionsClient.parse_session_entity_type_path + ) + transition_route_group_path = staticmethod( + SessionsClient.transition_route_group_path + ) + parse_transition_route_group_path = staticmethod( + SessionsClient.parse_transition_route_group_path + ) + webhook_path = staticmethod(SessionsClient.webhook_path) + parse_webhook_path = staticmethod(SessionsClient.parse_webhook_path) - intent_path = staticmethod(SessionsClient.intent_path) + common_billing_account_path = staticmethod( + SessionsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + SessionsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(SessionsClient.common_folder_path) + parse_common_folder_path = staticmethod(SessionsClient.parse_common_folder_path) + + common_organization_path = staticmethod(SessionsClient.common_organization_path) + parse_common_organization_path = staticmethod( + SessionsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(SessionsClient.common_project_path) + parse_common_project_path = staticmethod(SessionsClient.parse_common_project_path) + + common_location_path = staticmethod(SessionsClient.common_location_path) + parse_common_location_path = staticmethod(SessionsClient.parse_common_location_path) from_service_account_file = SessionsClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> SessionsTransport: + """Return the transport used by the client instance. + + Returns: + SessionsTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(SessionsClient).get_transport_class, type(SessionsClient) ) @@ -82,16 +140,19 @@ def __init__( 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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + 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 @@ -172,7 +233,7 @@ def streaming_detect_intent( retry: retries.Retry = gapic_v1.method.DEFAULT, timeout: float = None, metadata: Sequence[Tuple[str, str]] = (), - ) -> AsyncIterable[session.StreamingDetectIntentResponse]: + ) -> Awaitable[AsyncIterable[session.StreamingDetectIntentResponse]]: r"""Processes a natural language query in audio format in a streaming fashion and returns structured, actionable data as a result. This method is only available via the @@ -184,7 +245,6 @@ def streaming_detect_intent( client to the [Sessions.StreamingDetectIntent][google.cloud.dialogflow.cx.v3beta1.Sessions.StreamingDetectIntent] method. - Multiple request messages should be sent in order: 1. The first message must contain @@ -192,19 +252,19 @@ def streaming_detect_intent( [query_input][google.cloud.dialogflow.cx.v3beta1.StreamingDetectIntentRequest.query_input] plus optionally [query_params][google.cloud.dialogflow.cx.v3beta1.StreamingDetectIntentRequest.query_params]. - If the client wants to receive an audio response, it + If the client wants to receive an audio response, it should also contain [output_audio_config][google.cloud.dialogflow.cx.v3beta1.StreamingDetectIntentRequest.output_audio_config]. 2. If [query_input][google.cloud.dialogflow.cx.v3beta1.StreamingDetectIntentRequest.query_input] was set to [query_input.audio.config][google.cloud.dialogflow.cx.v3beta1.AudioInput.config], - all subsequent messages must contain + all subsequent messages must contain [query_input.audio.audio][google.cloud.dialogflow.cx.v3beta1.AudioInput.audio] - to continue with Speech recognition. If you decide to - rather detect an intent from text input after you - already started Speech recognition, please send a - message with + to continue with Speech recognition. + If you decide to rather detect an intent from text + input after you already started Speech recognition, + please send a message with [query_input.text][google.cloud.dialogflow.cx.v3beta1.QueryInput.text]. However, note that: diff --git a/google/cloud/dialogflowcx_v3beta1/services/sessions/client.py b/google/cloud/dialogflowcx_v3beta1/services/sessions/client.py index 07a4adae..a116e029 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/sessions/client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/sessions/client.py @@ -16,17 +16,29 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Iterable, Iterator, Sequence, Tuple, Type, Union +from typing import ( + Callable, + Dict, + Optional, + Iterable, + Iterator, + Sequence, + Tuple, + Type, + Union, +) import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +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 @@ -131,6 +143,49 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> SessionsTransport: + """Return the transport used by the client instance. + + Returns: + SessionsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def entity_type_path( + project: str, location: str, agent: str, entity_type: str, + ) -> str: + """Return a fully-qualified entity_type string.""" + return "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + + @staticmethod + def parse_entity_type_path(path: str) -> Dict[str, str]: + """Parse a entity_type path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/entityTypes/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def flow_path(project: str, location: str, agent: str, flow: str,) -> str: + """Return a fully-qualified flow string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + + @staticmethod + def parse_flow_path(path: str) -> Dict[str, str]: + """Parse a flow path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + @staticmethod def intent_path(project: str, location: str, agent: str, intent: str,) -> str: """Return a fully-qualified intent string.""" @@ -147,6 +202,40 @@ def parse_intent_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def page_path( + project: str, location: str, agent: str, flow: str, page: str, + ) -> str: + """Return a fully-qualified page string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + + @staticmethod + def parse_page_path(path: str) -> Dict[str, str]: + """Parse a page path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/pages/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def session_path(project: str, location: str, agent: str, session: str,) -> str: + """Return a fully-qualified session string.""" + return "projects/{project}/locations/{location}/agents/{agent}/sessions/{session}".format( + project=project, location=location, agent=agent, session=session, + ) + + @staticmethod + def parse_session_path(path: str) -> Dict[str, str]: + """Parse a session path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/sessions/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + @staticmethod def session_entity_type_path( project: str, location: str, agent: str, session: str, entity_type: str, @@ -169,12 +258,109 @@ def parse_session_entity_type_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def transition_route_group_path( + project: str, location: str, agent: str, flow: str, transition_route_group: str, + ) -> str: + """Return a fully-qualified transition_route_group string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/transitionRouteGroups/{transition_route_group}".format( + project=project, + location=location, + agent=agent, + flow=flow, + transition_route_group=transition_route_group, + ) + + @staticmethod + def parse_transition_route_group_path(path: str) -> Dict[str, str]: + """Parse a transition_route_group path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/transitionRouteGroups/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def webhook_path(project: str, location: str, agent: str, webhook: str,) -> str: + """Return a fully-qualified webhook string.""" + return "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + + @staticmethod + def parse_webhook_path(path: str) -> Dict[str, str]: + """Parse a webhook path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/webhooks/(?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: credentials.Credentials = None, - transport: Union[str, SessionsTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, SessionsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the sessions client. @@ -188,23 +374,26 @@ def __init__( transport (Union[str, ~.SessionsTransport]): 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. + client_options (client_options_lib.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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - 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 + 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: @@ -212,29 +401,47 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + 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")) + ) + + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -258,10 +465,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + ssl_channel_credentials=ssl_credentials, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -340,7 +546,6 @@ def streaming_detect_intent( client to the [Sessions.StreamingDetectIntent][google.cloud.dialogflow.cx.v3beta1.Sessions.StreamingDetectIntent] method. - Multiple request messages should be sent in order: 1. The first message must contain @@ -348,19 +553,19 @@ def streaming_detect_intent( [query_input][google.cloud.dialogflow.cx.v3beta1.StreamingDetectIntentRequest.query_input] plus optionally [query_params][google.cloud.dialogflow.cx.v3beta1.StreamingDetectIntentRequest.query_params]. - If the client wants to receive an audio response, it + If the client wants to receive an audio response, it should also contain [output_audio_config][google.cloud.dialogflow.cx.v3beta1.StreamingDetectIntentRequest.output_audio_config]. 2. If [query_input][google.cloud.dialogflow.cx.v3beta1.StreamingDetectIntentRequest.query_input] was set to [query_input.audio.config][google.cloud.dialogflow.cx.v3beta1.AudioInput.config], - all subsequent messages must contain + all subsequent messages must contain [query_input.audio.audio][google.cloud.dialogflow.cx.v3beta1.AudioInput.audio] - to continue with Speech recognition. If you decide to - rather detect an intent from text input after you - already started Speech recognition, please send a - message with + to continue with Speech recognition. + If you decide to rather detect an intent from text + input after you already started Speech recognition, + please send a message with [query_input.text][google.cloud.dialogflow.cx.v3beta1.QueryInput.text]. However, note that: diff --git a/google/cloud/dialogflowcx_v3beta1/services/sessions/transports/grpc.py b/google/cloud/dialogflowcx_v3beta1/services/sessions/transports/grpc.py index e3acc84d..e9943996 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/sessions/transports/grpc.py +++ b/google/cloud/dialogflowcx_v3beta1/services/sessions/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -23,7 +24,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.dialogflowcx_v3beta1.types import session @@ -59,6 +59,7 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -79,20 +80,22 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -101,6 +104,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -108,7 +113,13 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -139,6 +150,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) self._stubs = {} # type: Dict[str, Callable] @@ -199,19 +228,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.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 designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/dialogflowcx_v3beta1/services/sessions/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3beta1/services/sessions/transports/grpc_asyncio.py index 7009dedb..8daf8f78 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/sessions/transports/grpc_asyncio.py +++ b/google/cloud/dialogflowcx_v3beta1/services/sessions/transports/grpc_asyncio.py @@ -15,10 +15,12 @@ # 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 @@ -101,6 +103,7 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -122,14 +125,16 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -144,6 +149,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -151,13 +158,24 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443" ) + 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: @@ -177,6 +195,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) # Run the base constructor. super().__init__( @@ -197,13 +233,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/async_client.py b/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/async_client.py index 877c363b..ff955432 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/async_client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/async_client.py @@ -51,13 +51,64 @@ class TransitionRouteGroupsAsyncClient: DEFAULT_ENDPOINT = TransitionRouteGroupsClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = TransitionRouteGroupsClient.DEFAULT_MTLS_ENDPOINT + flow_path = staticmethod(TransitionRouteGroupsClient.flow_path) + parse_flow_path = staticmethod(TransitionRouteGroupsClient.parse_flow_path) + intent_path = staticmethod(TransitionRouteGroupsClient.intent_path) + parse_intent_path = staticmethod(TransitionRouteGroupsClient.parse_intent_path) + page_path = staticmethod(TransitionRouteGroupsClient.page_path) + parse_page_path = staticmethod(TransitionRouteGroupsClient.parse_page_path) transition_route_group_path = staticmethod( TransitionRouteGroupsClient.transition_route_group_path ) + parse_transition_route_group_path = staticmethod( + TransitionRouteGroupsClient.parse_transition_route_group_path + ) + webhook_path = staticmethod(TransitionRouteGroupsClient.webhook_path) + parse_webhook_path = staticmethod(TransitionRouteGroupsClient.parse_webhook_path) + + common_billing_account_path = staticmethod( + TransitionRouteGroupsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + TransitionRouteGroupsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(TransitionRouteGroupsClient.common_folder_path) + parse_common_folder_path = staticmethod( + TransitionRouteGroupsClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + TransitionRouteGroupsClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + TransitionRouteGroupsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(TransitionRouteGroupsClient.common_project_path) + parse_common_project_path = staticmethod( + TransitionRouteGroupsClient.parse_common_project_path + ) + + common_location_path = staticmethod( + TransitionRouteGroupsClient.common_location_path + ) + parse_common_location_path = staticmethod( + TransitionRouteGroupsClient.parse_common_location_path + ) from_service_account_file = TransitionRouteGroupsClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> TransitionRouteGroupsTransport: + """Return the transport used by the client instance. + + Returns: + TransitionRouteGroupsTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(TransitionRouteGroupsClient).get_transport_class, type(TransitionRouteGroupsClient), @@ -85,16 +136,19 @@ def __init__( 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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + 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 @@ -150,7 +204,8 @@ async def list_transition_route_groups( # 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. - if request is not None and any([parent]): + 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." @@ -232,7 +287,8 @@ async def get_transition_route_group( # 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. - if request is not None and any([name]): + 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." @@ -316,7 +372,8 @@ async def create_transition_route_group( # 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. - if request is not None and any([parent, transition_route_group]): + has_flattened_params = any([parent, transition_route_group]) + 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." @@ -399,7 +456,8 @@ async def update_transition_route_group( # 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. - if request is not None and any([transition_route_group, update_mask]): + has_flattened_params = any([transition_route_group, update_mask]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -471,7 +529,8 @@ async def delete_transition_route_group( # 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. - if request is not None and any([name]): + 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." diff --git a/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/client.py b/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/client.py index 00f1443a..6594b050 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/client.py @@ -16,17 +16,19 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +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 @@ -137,6 +139,65 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> TransitionRouteGroupsTransport: + """Return the transport used by the client instance. + + Returns: + TransitionRouteGroupsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def flow_path(project: str, location: str, agent: str, flow: str,) -> str: + """Return a fully-qualified flow string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + + @staticmethod + def parse_flow_path(path: str) -> Dict[str, str]: + """Parse a flow path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def intent_path(project: str, location: str, agent: str, intent: str,) -> str: + """Return a fully-qualified intent string.""" + return "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + + @staticmethod + def parse_intent_path(path: str) -> Dict[str, str]: + """Parse a intent path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/intents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def page_path( + project: str, location: str, agent: str, flow: str, page: str, + ) -> str: + """Return a fully-qualified page string.""" + return "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + + @staticmethod + def parse_page_path(path: str) -> Dict[str, str]: + """Parse a page path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/flows/(?P.+?)/pages/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + @staticmethod def transition_route_group_path( project: str, location: str, agent: str, flow: str, transition_route_group: str, @@ -159,12 +220,87 @@ def parse_transition_route_group_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def webhook_path(project: str, location: str, agent: str, webhook: str,) -> str: + """Return a fully-qualified webhook string.""" + return "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + + @staticmethod + def parse_webhook_path(path: str) -> Dict[str, str]: + """Parse a webhook path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/locations/(?P.+?)/agents/(?P.+?)/webhooks/(?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: credentials.Credentials = None, - transport: Union[str, TransitionRouteGroupsTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, TransitionRouteGroupsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the transition route groups client. @@ -178,23 +314,26 @@ def __init__( transport (Union[str, ~.TransitionRouteGroupsTransport]): 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. + client_options (client_options_lib.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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - 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 + 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: @@ -202,29 +341,47 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + 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")) + ) - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -248,10 +405,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + ssl_channel_credentials=ssl_credentials, quota_project_id=client_options.quota_project_id, client_info=client_info, ) diff --git a/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/grpc.py b/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/grpc.py index 0c13abe9..5898945a 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/grpc.py +++ b/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -23,7 +24,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.dialogflowcx_v3beta1.types import transition_route_group @@ -61,6 +61,7 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -81,20 +82,22 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -103,6 +106,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -110,7 +115,13 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -141,6 +152,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) self._stubs = {} # type: Dict[str, Callable] @@ -201,19 +230,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.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 designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/grpc_asyncio.py index 0170c321..38197156 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/grpc_asyncio.py +++ b/google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/grpc_asyncio.py @@ -15,10 +15,12 @@ # 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 @@ -103,6 +105,7 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -124,14 +127,16 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -146,6 +151,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -153,13 +160,24 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443" ) + 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: @@ -179,6 +197,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) # Run the base constructor. super().__init__( @@ -199,13 +235,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/dialogflowcx_v3beta1/services/versions/async_client.py b/google/cloud/dialogflowcx_v3beta1/services/versions/async_client.py index 7fbdce92..3fdb50ef 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/versions/async_client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/versions/async_client.py @@ -28,8 +28,8 @@ from google.auth import credentials # type: ignore from google.oauth2 import service_account # type: ignore -from google.api_core import operation -from google.api_core import operation_async +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore from google.cloud.dialogflowcx_v3beta1.services.versions import pagers from google.cloud.dialogflowcx_v3beta1.types import flow from google.cloud.dialogflowcx_v3beta1.types import version @@ -55,10 +55,41 @@ class VersionsAsyncClient: DEFAULT_MTLS_ENDPOINT = VersionsClient.DEFAULT_MTLS_ENDPOINT version_path = staticmethod(VersionsClient.version_path) + parse_version_path = staticmethod(VersionsClient.parse_version_path) + + common_billing_account_path = staticmethod( + VersionsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + VersionsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(VersionsClient.common_folder_path) + parse_common_folder_path = staticmethod(VersionsClient.parse_common_folder_path) + + common_organization_path = staticmethod(VersionsClient.common_organization_path) + parse_common_organization_path = staticmethod( + VersionsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(VersionsClient.common_project_path) + parse_common_project_path = staticmethod(VersionsClient.parse_common_project_path) + + common_location_path = staticmethod(VersionsClient.common_location_path) + parse_common_location_path = staticmethod(VersionsClient.parse_common_location_path) from_service_account_file = VersionsClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> VersionsTransport: + """Return the transport used by the client instance. + + Returns: + VersionsTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(VersionsClient).get_transport_class, type(VersionsClient) ) @@ -85,16 +116,19 @@ def __init__( 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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + 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 @@ -151,7 +185,8 @@ async def list_versions( # 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. - if request is not None and any([parent]): + 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." @@ -229,7 +264,8 @@ async def get_version( # 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. - if request is not None and any([name]): + 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." @@ -315,7 +351,8 @@ async def create_version( # 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. - if request is not None and any([parent, version]): + has_flattened_params = any([parent, version]) + 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." @@ -402,7 +439,8 @@ async def update_version( # 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. - if request is not None and any([version, update_mask]): + has_flattened_params = any([version, update_mask]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -474,7 +512,8 @@ async def delete_version( # 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. - if request is not None and any([name]): + 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." @@ -561,7 +600,8 @@ async def load_version( # 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. - if request is not None and any([name]): + 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." diff --git a/google/cloud/dialogflowcx_v3beta1/services/versions/client.py b/google/cloud/dialogflowcx_v3beta1/services/versions/client.py index 3bb7e924..92c5145a 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/versions/client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/versions/client.py @@ -16,22 +16,24 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +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.api_core import operation -from google.api_core import operation_async +from google.api_core import operation # type: ignore +from google.api_core import operation_async # type: ignore from google.cloud.dialogflowcx_v3beta1.services.versions import pagers from google.cloud.dialogflowcx_v3beta1.types import flow from google.cloud.dialogflowcx_v3beta1.types import version @@ -136,6 +138,15 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> VersionsTransport: + """Return the transport used by the client instance. + + Returns: + VersionsTransport: The transport used by the client instance. + """ + return self._transport + @staticmethod def version_path( project: str, location: str, agent: str, flow: str, version: str, @@ -154,12 +165,71 @@ def parse_version_path(path: str) -> Dict[str, str]: ) 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: credentials.Credentials = None, - transport: Union[str, VersionsTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, VersionsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the versions client. @@ -173,23 +243,26 @@ def __init__( transport (Union[str, ~.VersionsTransport]): 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. + client_options (client_options_lib.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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - 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 + 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: @@ -197,29 +270,47 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + 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")) + ) - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -243,10 +334,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + ssl_channel_credentials=ssl_credentials, quota_project_id=client_options.quota_project_id, client_info=client_info, ) diff --git a/google/cloud/dialogflowcx_v3beta1/services/versions/transports/grpc.py b/google/cloud/dialogflowcx_v3beta1/services/versions/transports/grpc.py index 8b1c2bab..e7da5ca1 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/versions/transports/grpc.py +++ b/google/cloud/dialogflowcx_v3beta1/services/versions/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -24,7 +25,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.dialogflowcx_v3beta1.types import version @@ -61,6 +61,7 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -81,20 +82,22 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -103,6 +106,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -110,7 +115,13 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -141,6 +152,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) self._stubs = {} # type: Dict[str, Callable] @@ -201,19 +230,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.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 designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/dialogflowcx_v3beta1/services/versions/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3beta1/services/versions/transports/grpc_asyncio.py index 6e154674..97ffe378 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/versions/transports/grpc_asyncio.py +++ b/google/cloud/dialogflowcx_v3beta1/services/versions/transports/grpc_asyncio.py @@ -15,11 +15,13 @@ # 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.api_core import operations_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 @@ -103,6 +105,7 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -124,14 +127,16 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -146,6 +151,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -153,13 +160,24 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443" ) + 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: @@ -179,6 +197,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) # Run the base constructor. super().__init__( @@ -199,13 +235,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/dialogflowcx_v3beta1/services/webhooks/async_client.py b/google/cloud/dialogflowcx_v3beta1/services/webhooks/async_client.py index 157f2ec4..bb18f096 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/webhooks/async_client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/webhooks/async_client.py @@ -50,10 +50,41 @@ class WebhooksAsyncClient: DEFAULT_MTLS_ENDPOINT = WebhooksClient.DEFAULT_MTLS_ENDPOINT webhook_path = staticmethod(WebhooksClient.webhook_path) + parse_webhook_path = staticmethod(WebhooksClient.parse_webhook_path) + + common_billing_account_path = staticmethod( + WebhooksClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + WebhooksClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(WebhooksClient.common_folder_path) + parse_common_folder_path = staticmethod(WebhooksClient.parse_common_folder_path) + + common_organization_path = staticmethod(WebhooksClient.common_organization_path) + parse_common_organization_path = staticmethod( + WebhooksClient.parse_common_organization_path + ) + + common_project_path = staticmethod(WebhooksClient.common_project_path) + parse_common_project_path = staticmethod(WebhooksClient.parse_common_project_path) + + common_location_path = staticmethod(WebhooksClient.common_location_path) + parse_common_location_path = staticmethod(WebhooksClient.parse_common_location_path) from_service_account_file = WebhooksClient.from_service_account_file from_service_account_json = from_service_account_file + @property + def transport(self) -> WebhooksTransport: + """Return the transport used by the client instance. + + Returns: + WebhooksTransport: The transport used by the client instance. + """ + return self._client.transport + get_transport_class = functools.partial( type(WebhooksClient).get_transport_class, type(WebhooksClient) ) @@ -80,16 +111,19 @@ def __init__( 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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. + 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 @@ -144,7 +178,8 @@ async def list_webhooks( # 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. - if request is not None and any([parent]): + 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." @@ -226,7 +261,8 @@ async def get_webhook( # 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. - if request is not None and any([name]): + 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." @@ -308,7 +344,8 @@ async def create_webhook( # 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. - if request is not None and any([parent, webhook]): + has_flattened_params = any([parent, webhook]) + 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." @@ -393,7 +430,8 @@ async def update_webhook( # 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. - if request is not None and any([webhook, update_mask]): + has_flattened_params = any([webhook, update_mask]) + if request is not None and has_flattened_params: raise ValueError( "If the `request` argument is set, then none of " "the individual field arguments should be set." @@ -462,7 +500,8 @@ async def delete_webhook( # 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. - if request is not None and any([name]): + 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." diff --git a/google/cloud/dialogflowcx_v3beta1/services/webhooks/client.py b/google/cloud/dialogflowcx_v3beta1/services/webhooks/client.py index 4496eeab..96720000 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/webhooks/client.py +++ b/google/cloud/dialogflowcx_v3beta1/services/webhooks/client.py @@ -16,17 +16,19 @@ # from collections import OrderedDict +from distutils import util import os import re -from typing import Callable, Dict, Sequence, Tuple, Type, Union +from typing import Callable, Dict, Optional, Sequence, Tuple, Type, Union import pkg_resources -import google.api_core.client_options as ClientOptions # type: ignore +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 @@ -131,6 +133,15 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): from_service_account_json = from_service_account_file + @property + def transport(self) -> WebhooksTransport: + """Return the transport used by the client instance. + + Returns: + WebhooksTransport: The transport used by the client instance. + """ + return self._transport + @staticmethod def webhook_path(project: str, location: str, agent: str, webhook: str,) -> str: """Return a fully-qualified webhook string.""" @@ -147,12 +158,71 @@ def parse_webhook_path(path: str) -> Dict[str, str]: ) 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: credentials.Credentials = None, - transport: Union[str, WebhooksTransport] = None, - client_options: ClientOptions = None, + credentials: Optional[credentials.Credentials] = None, + transport: Union[str, WebhooksTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: """Instantiate the webhooks client. @@ -166,23 +236,26 @@ def __init__( transport (Union[str, ~.WebhooksTransport]): 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. + client_options (client_options_lib.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 + 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, this is the default value for - the environment variable) and "auto" (auto switch to the default - mTLS endpoint if client SSL credentials is present). However, - the ``api_endpoint`` property takes precedence if provided. - (2) The ``client_cert_source`` property is used to provide client - SSL credentials for mutual TLS transport. If not provided, the - default SSL credentials will be used if present. - 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 + 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: @@ -190,29 +263,47 @@ def __init__( creation failed for any reason. """ if isinstance(client_options, dict): - client_options = ClientOptions.from_dict(client_options) + client_options = client_options_lib.from_dict(client_options) if client_options is None: - client_options = ClientOptions.ClientOptions() + 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")) + ) - if client_options.api_endpoint is None: - use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS", "never") + ssl_credentials = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + import grpc # type: ignore + + cert, key = client_options.client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + is_mtls = True + else: + creds = SslCredentials() + is_mtls = creds.is_mtls + ssl_credentials = creds.ssl_credentials 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": - client_options.api_endpoint = self.DEFAULT_ENDPOINT + api_endpoint = self.DEFAULT_ENDPOINT elif use_mtls_env == "always": - client_options.api_endpoint = self.DEFAULT_MTLS_ENDPOINT + api_endpoint = self.DEFAULT_MTLS_ENDPOINT elif use_mtls_env == "auto": - has_client_cert_source = ( - client_options.client_cert_source is not None - or mtls.has_default_client_cert_source() - ) - client_options.api_endpoint = ( - self.DEFAULT_MTLS_ENDPOINT - if has_client_cert_source - else self.DEFAULT_ENDPOINT + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT ) else: raise MutualTLSChannelError( - "Unsupported GOOGLE_API_USE_MTLS value. Accepted values: never, auto, always" + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" ) # Save or instantiate the transport. @@ -236,10 +327,9 @@ def __init__( self._transport = Transport( credentials=credentials, credentials_file=client_options.credentials_file, - host=client_options.api_endpoint, + host=api_endpoint, scopes=client_options.scopes, - api_mtls_endpoint=client_options.api_endpoint, - client_cert_source=client_options.client_cert_source, + ssl_channel_credentials=ssl_credentials, quota_project_id=client_options.quota_project_id, client_info=client_info, ) diff --git a/google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/grpc.py b/google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/grpc.py index 47f17c6b..3125edd2 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/grpc.py +++ b/google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/grpc.py @@ -15,6 +15,7 @@ # limitations under the License. # +import warnings from typing import Callable, Dict, Optional, Sequence, Tuple from google.api_core import grpc_helpers # type: ignore @@ -23,7 +24,6 @@ from google.auth import credentials # type: ignore from google.auth.transport.grpc import SslCredentials # type: ignore - import grpc # type: ignore from google.cloud.dialogflowcx_v3beta1.types import webhook @@ -59,6 +59,7 @@ def __init__( channel: grpc.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -79,20 +80,22 @@ def __init__( ignored if ``channel`` is provided. channel (Optional[grpc.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. - client_info (google.api_core.gapic_v1.client_info.ClientInfo): - The client info used to send a user-agent string along with - API requests. If ``None``, then default info will be used. - Generally, you only need to set this if you're developing + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing your own client library. Raises: @@ -101,6 +104,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -108,7 +113,13 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -139,6 +150,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) self._stubs = {} # type: Dict[str, Callable] @@ -199,19 +228,8 @@ def create_channel( @property def grpc_channel(self) -> grpc.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 designed to connect to this service. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - - # Return the channel from cache. return self._grpc_channel @property diff --git a/google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/grpc_asyncio.py b/google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/grpc_asyncio.py index d0cc5976..04ea7da1 100644 --- a/google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/grpc_asyncio.py +++ b/google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/grpc_asyncio.py @@ -15,10 +15,12 @@ # 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 @@ -101,6 +103,7 @@ def __init__( channel: aio.Channel = None, api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, + ssl_channel_credentials: grpc.ChannelCredentials = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -122,14 +125,16 @@ def __init__( are passed to :func:`google.auth.default`. channel (Optional[aio.Channel]): A ``Channel`` instance through which to make calls. - api_mtls_endpoint (Optional[str]): The mutual TLS endpoint. If - provided, it overrides the ``host`` argument and tries to create + 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]]]): 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. + 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. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -144,6 +149,8 @@ def __init__( google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` and ``credentials_file`` are passed. """ + self._ssl_channel_credentials = ssl_channel_credentials + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -151,13 +158,24 @@ def __init__( # If a channel was explicitly provided, set it. self._grpc_channel = channel + self._ssl_channel_credentials = None elif api_mtls_endpoint: + warnings.warn( + "api_mtls_endpoint and client_cert_source are deprecated", + DeprecationWarning, + ) + host = ( api_mtls_endpoint if ":" in api_mtls_endpoint else api_mtls_endpoint + ":443" ) + 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: @@ -177,6 +195,24 @@ def __init__( scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, ) + 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 + ) + + # 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_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + ) # Run the base constructor. super().__init__( @@ -197,13 +233,6 @@ def grpc_channel(self) -> aio.Channel: This property caches on the instance; repeated calls return the same channel. """ - # Sanity check: Only create a new channel if we do not already - # have one. - if not hasattr(self, "_grpc_channel"): - self._grpc_channel = self.create_channel( - self._host, credentials=self._credentials, - ) - # Return the channel from cache. return self._grpc_channel diff --git a/google/cloud/dialogflowcx_v3beta1/types/__init__.py b/google/cloud/dialogflowcx_v3beta1/types/__init__.py index 8a08405d..c08c5d09 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/__init__.py +++ b/google/cloud/dialogflowcx_v3beta1/types/__init__.py @@ -110,11 +110,14 @@ TextInput, IntentInput, AudioInput, + EventInput, + DtmfInput, Match, MatchIntentRequest, MatchIntentResponse, FulfillIntentRequest, FulfillIntentResponse, + SentimentAnalysisResult, ) from .transition_route_group import ( TransitionRouteGroup, @@ -230,11 +233,14 @@ "TextInput", "IntentInput", "AudioInput", + "EventInput", + "DtmfInput", "Match", "MatchIntentRequest", "MatchIntentResponse", "FulfillIntentRequest", "FulfillIntentResponse", + "SentimentAnalysisResult", "TransitionRouteGroup", "ListTransitionRouteGroupsRequest", "ListTransitionRouteGroupsResponse", diff --git a/google/cloud/dialogflowcx_v3beta1/types/agent.py b/google/cloud/dialogflowcx_v3beta1/types/agent.py index e2af4e43..ac3025af 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/agent.py +++ b/google/cloud/dialogflowcx_v3beta1/types/agent.py @@ -126,7 +126,7 @@ class Agent(proto.Message): avatar_uri = proto.Field(proto.STRING, number=7) speech_to_text_settings = proto.Field( - proto.MESSAGE, number=13, message=SpeechToTextSettings, + proto.MESSAGE, number=13, message="SpeechToTextSettings", ) start_flow = proto.Field(proto.STRING, number=16) @@ -177,7 +177,7 @@ class ListAgentsResponse(proto.Message): def raw_page(self): return self - agents = proto.RepeatedField(proto.MESSAGE, number=1, message=Agent,) + agents = proto.RepeatedField(proto.MESSAGE, number=1, message="Agent",) next_page_token = proto.Field(proto.STRING, number=2) @@ -209,7 +209,7 @@ class CreateAgentRequest(proto.Message): parent = proto.Field(proto.STRING, number=1) - agent = proto.Field(proto.MESSAGE, number=2, message=Agent,) + agent = proto.Field(proto.MESSAGE, number=2, message="Agent",) class UpdateAgentRequest(proto.Message): @@ -225,7 +225,7 @@ class UpdateAgentRequest(proto.Message): updated. """ - agent = proto.Field(proto.MESSAGE, number=1, message=Agent,) + agent = proto.Field(proto.MESSAGE, number=1, message="Agent",) update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) diff --git a/google/cloud/dialogflowcx_v3beta1/types/audio_config.py b/google/cloud/dialogflowcx_v3beta1/types/audio_config.py index b5b1742b..844c3edb 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/audio_config.py +++ b/google/cloud/dialogflowcx_v3beta1/types/audio_config.py @@ -269,7 +269,7 @@ class SynthesizeSpeechConfig(proto.Message): effects_profile_id = proto.RepeatedField(proto.STRING, number=5) - voice = proto.Field(proto.MESSAGE, number=4, message=VoiceSelectionParams,) + voice = proto.Field(proto.MESSAGE, number=4, message="VoiceSelectionParams",) class OutputAudioConfig(proto.Message): @@ -299,7 +299,7 @@ class OutputAudioConfig(proto.Message): sample_rate_hertz = proto.Field(proto.INT32, number=2) synthesize_speech_config = proto.Field( - proto.MESSAGE, number=3, message=SynthesizeSpeechConfig, + proto.MESSAGE, number=3, message="SynthesizeSpeechConfig", ) diff --git a/google/cloud/dialogflowcx_v3beta1/types/entity_type.py b/google/cloud/dialogflowcx_v3beta1/types/entity_type.py index 6595bc75..ca80a8a8 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/entity_type.py +++ b/google/cloud/dialogflowcx_v3beta1/types/entity_type.py @@ -228,7 +228,7 @@ class ListEntityTypesResponse(proto.Message): def raw_page(self): return self - entity_types = proto.RepeatedField(proto.MESSAGE, number=1, message=EntityType,) + entity_types = proto.RepeatedField(proto.MESSAGE, number=1, message="EntityType",) next_page_token = proto.Field(proto.STRING, number=2) @@ -287,7 +287,7 @@ class CreateEntityTypeRequest(proto.Message): parent = proto.Field(proto.STRING, number=1) - entity_type = proto.Field(proto.MESSAGE, number=2, message=EntityType,) + entity_type = proto.Field(proto.MESSAGE, number=2, message="EntityType",) language_code = proto.Field(proto.STRING, number=3) @@ -315,7 +315,7 @@ class UpdateEntityTypeRequest(proto.Message): The mask to control which fields get updated. """ - entity_type = proto.Field(proto.MESSAGE, number=1, message=EntityType,) + entity_type = proto.Field(proto.MESSAGE, number=1, message="EntityType",) language_code = proto.Field(proto.STRING, number=2) diff --git a/google/cloud/dialogflowcx_v3beta1/types/environment.py b/google/cloud/dialogflowcx_v3beta1/types/environment.py index 86e80914..9d6426c3 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/environment.py +++ b/google/cloud/dialogflowcx_v3beta1/types/environment.py @@ -141,7 +141,7 @@ class ListEnvironmentsResponse(proto.Message): def raw_page(self): return self - environments = proto.RepeatedField(proto.MESSAGE, number=1, message=Environment,) + environments = proto.RepeatedField(proto.MESSAGE, number=1, message="Environment",) next_page_token = proto.Field(proto.STRING, number=2) @@ -179,7 +179,7 @@ class CreateEnvironmentRequest(proto.Message): parent = proto.Field(proto.STRING, number=1) - environment = proto.Field(proto.MESSAGE, number=2, message=Environment,) + environment = proto.Field(proto.MESSAGE, number=2, message="Environment",) class UpdateEnvironmentRequest(proto.Message): @@ -194,7 +194,7 @@ class UpdateEnvironmentRequest(proto.Message): get updated. """ - environment = proto.Field(proto.MESSAGE, number=1, message=Environment,) + environment = proto.Field(proto.MESSAGE, number=1, message="Environment",) update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) @@ -257,7 +257,7 @@ class LookupEnvironmentHistoryResponse(proto.Message): def raw_page(self): return self - environments = proto.RepeatedField(proto.MESSAGE, number=1, message=Environment,) + environments = proto.RepeatedField(proto.MESSAGE, number=1, message="Environment",) next_page_token = proto.Field(proto.STRING, number=2) diff --git a/google/cloud/dialogflowcx_v3beta1/types/flow.py b/google/cloud/dialogflowcx_v3beta1/types/flow.py index 11b8341a..7f97a338 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/flow.py +++ b/google/cloud/dialogflowcx_v3beta1/types/flow.py @@ -56,9 +56,6 @@ class NluSettings(proto.Message): to 0.0, the default of 0.3 is used. model_training_mode (~.gcdc_flow.NluSettings.ModelTrainingMode): Indicates NLU model training mode. - enable_spell_correction (bool): - Indicates if automatic spell correction is - enabled in detect intent requests. """ class ModelType(proto.Enum): @@ -79,8 +76,6 @@ class ModelTrainingMode(proto.Enum): model_training_mode = proto.Field(proto.ENUM, number=4, enum=ModelTrainingMode,) - enable_spell_correction = proto.Field(proto.BOOL, number=5) - class Flow(proto.Message): r"""Flows represents the conversation flows when you build your @@ -164,7 +159,7 @@ class Flow(proto.Message): proto.MESSAGE, number=10, message=page.EventHandler, ) - nlu_settings = proto.Field(proto.MESSAGE, number=11, message=NluSettings,) + nlu_settings = proto.Field(proto.MESSAGE, number=11, message="NluSettings",) class CreateFlowRequest(proto.Message): @@ -192,7 +187,7 @@ class CreateFlowRequest(proto.Message): parent = proto.Field(proto.STRING, number=1) - flow = proto.Field(proto.MESSAGE, number=2, message=Flow,) + flow = proto.Field(proto.MESSAGE, number=2, message="Flow",) language_code = proto.Field(proto.STRING, number=3) @@ -279,7 +274,7 @@ class ListFlowsResponse(proto.Message): def raw_page(self): return self - flows = proto.RepeatedField(proto.MESSAGE, number=1, message=Flow,) + flows = proto.RepeatedField(proto.MESSAGE, number=1, message="Flow",) next_page_token = proto.Field(proto.STRING, number=2) @@ -334,7 +329,7 @@ class UpdateFlowRequest(proto.Message): before they can be used. """ - flow = proto.Field(proto.MESSAGE, number=1, message=Flow,) + flow = proto.Field(proto.MESSAGE, number=1, message="Flow",) update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) diff --git a/google/cloud/dialogflowcx_v3beta1/types/intent.py b/google/cloud/dialogflowcx_v3beta1/types/intent.py index c8d3edd6..57175421 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/intent.py +++ b/google/cloud/dialogflowcx_v3beta1/types/intent.py @@ -90,6 +90,26 @@ class Intent(proto.Message): mistakenly matched, since training phrases assigned to fallback intents act as negative examples that triggers no-match event. + labels (Sequence[~.gcdc_intent.Intent.LabelsEntry]): + Optional. The key/value metadata to label an intent. Labels + can contain lowercase letters, digits and the symbols '-' + and '_'. International characters are allowed, including + letters from unicase alphabets. Keys must start with a + letter. Keys and values can be no longer than 63 characters + and no more than 128 bytes. + + Prefix "sys-" is reserved for Dialogflow defined labels. + Currently allowed Dialogflow defined labels include: + + - sys-head + - sys-contextual The above labels do not require value. + "sys-head" means the intent is a head intent. + "sys-contextual" means the intent is a contextual intent. + description (str): + Optional. Human readable description for + better understanding an intent like its scope, + content, result etc. Maximum character limit: + 140 characters. """ class TrainingPhrase(proto.Message): @@ -176,6 +196,12 @@ class Parameter(proto.Message): is_list (bool): Indicates whether the parameter represents a list of values. + redact (bool): + Indicates whether the parameter content is + logged in text and audio. If it is set to true, + the parameter content will be replaced to + parameter id in both request and response. The + default value is false. """ id = proto.Field(proto.STRING, number=1) @@ -184,6 +210,8 @@ class Parameter(proto.Message): is_list = proto.Field(proto.BOOL, number=3) + redact = proto.Field(proto.BOOL, number=4) + name = proto.Field(proto.STRING, number=1) display_name = proto.Field(proto.STRING, number=2) @@ -198,6 +226,10 @@ class Parameter(proto.Message): is_fallback = proto.Field(proto.BOOL, number=6) + labels = proto.MapField(proto.STRING, proto.STRING, number=7) + + description = proto.Field(proto.STRING, number=8) + class ListIntentsRequest(proto.Message): r"""The request message for @@ -258,7 +290,7 @@ class ListIntentsResponse(proto.Message): def raw_page(self): return self - intents = proto.RepeatedField(proto.MESSAGE, number=1, message=Intent,) + intents = proto.RepeatedField(proto.MESSAGE, number=1, message="Intent",) next_page_token = proto.Field(proto.STRING, number=2) @@ -313,7 +345,7 @@ class CreateIntentRequest(proto.Message): parent = proto.Field(proto.STRING, number=1) - intent = proto.Field(proto.MESSAGE, number=2, message=Intent,) + intent = proto.Field(proto.MESSAGE, number=2, message="Intent",) language_code = proto.Field(proto.STRING, number=3) @@ -341,7 +373,7 @@ class UpdateIntentRequest(proto.Message): updated. """ - intent = proto.Field(proto.MESSAGE, number=1, message=Intent,) + intent = proto.Field(proto.MESSAGE, number=1, message="Intent",) language_code = proto.Field(proto.STRING, number=2) diff --git a/google/cloud/dialogflowcx_v3beta1/types/page.py b/google/cloud/dialogflowcx_v3beta1/types/page.py index c4c1b30c..857ff94f 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/page.py +++ b/google/cloud/dialogflowcx_v3beta1/types/page.py @@ -181,6 +181,12 @@ class Parameter(proto.Message): The default value of an optional parameter. If the parameter is required, the default value will be ignored. + redact (bool): + Indicates whether the parameter content is + logged in text and audio. If it is set to true, + the parameter content will be replaced to + parameter name in both request and response. The + default value is false. """ class FillBehavior(proto.Message): @@ -254,6 +260,8 @@ class FillBehavior(proto.Message): default_value = proto.Field(proto.MESSAGE, number=9, message=struct.Value,) + redact = proto.Field(proto.BOOL, number=11) + parameters = proto.RepeatedField(proto.MESSAGE, number=1, message=Parameter,) @@ -440,7 +448,7 @@ class ListPagesResponse(proto.Message): def raw_page(self): return self - pages = proto.RepeatedField(proto.MESSAGE, number=1, message=Page,) + pages = proto.RepeatedField(proto.MESSAGE, number=1, message="Page",) next_page_token = proto.Field(proto.STRING, number=2) @@ -507,7 +515,7 @@ class CreatePageRequest(proto.Message): parent = proto.Field(proto.STRING, number=1) - page = proto.Field(proto.MESSAGE, number=2, message=Page,) + page = proto.Field(proto.MESSAGE, number=2, message="Page",) language_code = proto.Field(proto.STRING, number=3) @@ -541,7 +549,7 @@ class UpdatePageRequest(proto.Message): updated. """ - page = proto.Field(proto.MESSAGE, number=1, message=Page,) + page = proto.Field(proto.MESSAGE, number=1, message="Page",) language_code = proto.Field(proto.STRING, number=2) diff --git a/google/cloud/dialogflowcx_v3beta1/types/response_message.py b/google/cloud/dialogflowcx_v3beta1/types/response_message.py index f834097a..ad0e2726 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/response_message.py +++ b/google/cloud/dialogflowcx_v3beta1/types/response_message.py @@ -167,17 +167,18 @@ class OutputAudioText(proto.Message): ssml (str): The SSML text to be synthesized. For more information, see `SSML `__. + allow_playback_interruption (bool): + Output only. Whether the playback of this + message can be interrupted by the end user's + speech and the client can then starts the next + Dialogflow request. """ text = proto.Field(proto.STRING, number=1, oneof="source") ssml = proto.Field(proto.STRING, number=2, oneof="source") - class EndInteraction(proto.Message): - r"""Indicates that interaction with the Dialogflow agent has - ended. This message is generated by Dialogflow only and not - supposed to be defined by the user. - """ + allow_playback_interruption = proto.Field(proto.BOOL, number=3) class MixedAudio(proto.Message): r"""Represents an audio message that is composed of both segments @@ -222,6 +223,12 @@ class Segment(proto.Message): proto.MESSAGE, number=1, message="ResponseMessage.MixedAudio.Segment", ) + class EndInteraction(proto.Message): + r"""Indicates that interaction with the Dialogflow agent has + ended. This message is generated by Dialogflow only and not + supposed to be defined by the user. + """ + class PlayAudio(proto.Message): r"""Specifies an audio clip to be played by the client as part of the response. diff --git a/google/cloud/dialogflowcx_v3beta1/types/session.py b/google/cloud/dialogflowcx_v3beta1/types/session.py index 7f5d3ee2..98a33ff2 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/session.py +++ b/google/cloud/dialogflowcx_v3beta1/types/session.py @@ -43,11 +43,14 @@ "TextInput", "IntentInput", "AudioInput", + "EventInput", + "DtmfInput", "Match", "MatchIntentRequest", "MatchIntentResponse", "FulfillIntentRequest", "FulfillIntentResponse", + "SentimentAnalysisResult", }, ) @@ -67,6 +70,9 @@ class DetectIntentRequest(proto.Message): appropriate ``Session ID``. It can be a random number or some type of session identifiers (preferably hashed). The length of the ``Session ID`` must not exceed 36 characters. + + For more information, see the `sessions + guide `__. query_params (~.gcdc_session.QueryParameters): The parameters of this query. query_input (~.gcdc_session.QueryInput): @@ -176,6 +182,9 @@ class StreamingDetectIntentRequest(proto.Message): some type of session identifiers (preferably hashed). The length of the ``Session ID`` must not exceed 36 characters. Note: session must be set in the first request. + + For more information, see the `sessions + guide `__. query_params (~.gcdc_session.QueryParameters): The parameters of this query. query_input (~.gcdc_session.QueryInput): @@ -222,7 +231,7 @@ class StreamingDetectIntentResponse(proto.Message): ) detect_intent_response = proto.Field( - proto.MESSAGE, number=2, oneof="response", message=DetectIntentResponse, + proto.MESSAGE, number=2, oneof="response", message="DetectIntentResponse", ) @@ -373,6 +382,10 @@ class QueryParameters(proto.Message): from composite entity property names to property values - Else: parameter value + analyze_query_text_sentiment (bool): + Configures whether sentiment analysis should + be performed. If not provided, sentiment + analysis is not performed. """ time_zone = proto.Field(proto.STRING, number=1) @@ -387,12 +400,17 @@ class QueryParameters(proto.Message): parameters = proto.Field(proto.MESSAGE, number=5, message=struct.Struct,) + analyze_query_text_sentiment = proto.Field(proto.BOOL, number=8) + class QueryInput(proto.Message): - r"""Represents the query input. It can contain either: + r"""Represents the query input. It can contain one of: 1. A conversational query in the form of text. 2. An intent query that specifies which intent to trigger. + 3. Natural language speech audio to be processed. + + 4. An event to be triggered. Attributes: text (~.gcdc_session.TextInput): @@ -402,6 +420,10 @@ class QueryInput(proto.Message): audio (~.gcdc_session.AudioInput): The natural language speech audio to be processed. + event (~.gcdc_session.EventInput): + The event to be triggered. + dtmf (~.gcdc_session.DtmfInput): + The DTMF event to be handled. language_code (str): Required. The language of the input. See `Language Support `__ @@ -416,6 +438,10 @@ class QueryInput(proto.Message): audio = proto.Field(proto.MESSAGE, number=5, oneof="input", message="AudioInput",) + event = proto.Field(proto.MESSAGE, number=6, oneof="input", message="EventInput",) + + dtmf = proto.Field(proto.MESSAGE, number=7, oneof="input", message="DtmfInput",) + language_code = proto.Field(proto.STRING, number=4) @@ -514,6 +540,11 @@ class QueryResult(proto.Message): this field could contain webhook call latency. The string keys of the Struct's fields map can change without notice. + sentiment_analysis_result (~.gcdc_session.SentimentAnalysisResult): + The sentiment analyss result, which depends on + [``analyze_query_text_sentiment``] + [google.cloud.dialogflow.cx.v3beta1.QueryParameters.analyze_query_text_sentiment], + specified in the request. """ text = proto.Field(proto.STRING, number=1, oneof="query") @@ -550,6 +581,10 @@ class QueryResult(proto.Message): diagnostic_info = proto.Field(proto.MESSAGE, number=10, message=struct.Struct,) + sentiment_analysis_result = proto.Field( + proto.MESSAGE, number=17, message="SentimentAnalysisResult", + ) + class TextInput(proto.Message): r"""Represents the natural language text to be processed. @@ -604,6 +639,32 @@ class AudioInput(proto.Message): audio = proto.Field(proto.BYTES, number=2) +class EventInput(proto.Message): + r"""Represents the event to trigger. + + Attributes: + event (str): + Name of the event. + """ + + event = proto.Field(proto.STRING, number=1) + + +class DtmfInput(proto.Message): + r"""Represents the input for dtmf event. + + Attributes: + digits (str): + The dtmf digits. + finish_digit (str): + The finish digit (if any). + """ + + digits = proto.Field(proto.STRING, number=1) + + finish_digit = proto.Field(proto.STRING, number=2) + + class Match(proto.Message): r"""Represents one match result of [MatchIntent][]. @@ -615,6 +676,10 @@ class Match(proto.Message): ``display_name``. Only filled for [``INTENT``][google.cloud.dialogflow.cx.v3beta1.Match.MatchType] match type. + event (str): + The event that matched the query. Only filled for + [``EVENT``][google.cloud.dialogflow.cx.v3beta1.Match.MatchType] + match type. parameters (~.struct.Struct): The collection of parameters extracted from the query. @@ -662,9 +727,12 @@ class MatchType(proto.Enum): PARAMETER_FILLING = 3 NO_MATCH = 4 NO_INPUT = 5 + EVENT = 6 intent = proto.Field(proto.MESSAGE, number=1, message=gcdc_intent.Intent,) + event = proto.Field(proto.STRING, number=6) + parameters = proto.Field(proto.MESSAGE, number=2, message=struct.Struct,) resolved_input = proto.Field(proto.STRING, number=3) @@ -689,6 +757,9 @@ class MatchIntentRequest(proto.Message): appropriate ``Session ID``. It can be a random number or some type of session identifiers (preferably hashed). The length of the ``Session ID`` must not exceed 36 characters. + + For more information, see the `sessions + guide `__. query_params (~.gcdc_session.QueryParameters): The parameters of this query. query_input (~.gcdc_session.QueryInput): @@ -697,9 +768,9 @@ class MatchIntentRequest(proto.Message): session = proto.Field(proto.STRING, number=1) - query_params = proto.Field(proto.MESSAGE, number=2, message=QueryParameters,) + query_params = proto.Field(proto.MESSAGE, number=2, message="QueryParameters",) - query_input = proto.Field(proto.MESSAGE, number=3, message=QueryInput,) + query_input = proto.Field(proto.MESSAGE, number=3, message="QueryInput",) class MatchIntentResponse(proto.Message): @@ -721,6 +792,10 @@ class MatchIntentResponse(proto.Message): audio][google.cloud.dialogflow.cx.v3beta1.AudioInput] was provided as input, this field will contain the trascript for the audio. + trigger_event (str): + If an [event][google.cloud.dialogflow.cx.v3beta1.EventInput] + was provided as input, this field will contain a copy of the + event name. matches (Sequence[~.gcdc_session.Match]): Match results, if more than one, ordered descendingly by the confidence we have that the @@ -737,7 +812,9 @@ class MatchIntentResponse(proto.Message): transcript = proto.Field(proto.STRING, number=3, oneof="query") - matches = proto.RepeatedField(proto.MESSAGE, number=4, message=Match,) + trigger_event = proto.Field(proto.STRING, number=6, oneof="query") + + matches = proto.RepeatedField(proto.MESSAGE, number=4, message="Match",) current_page = proto.Field(proto.MESSAGE, number=5, message=page.Page,) @@ -757,10 +834,10 @@ class FulfillIntentRequest(proto.Message): """ match_intent_request = proto.Field( - proto.MESSAGE, number=1, message=MatchIntentRequest, + proto.MESSAGE, number=1, message="MatchIntentRequest", ) - match = proto.Field(proto.MESSAGE, number=2, message=Match,) + match = proto.Field(proto.MESSAGE, number=2, message="Match",) output_audio_config = proto.Field( proto.MESSAGE, number=3, message=audio_config.OutputAudioConfig, @@ -798,7 +875,7 @@ class FulfillIntentResponse(proto.Message): response_id = proto.Field(proto.STRING, number=1) - query_result = proto.Field(proto.MESSAGE, number=2, message=QueryResult,) + query_result = proto.Field(proto.MESSAGE, number=2, message="QueryResult",) output_audio = proto.Field(proto.BYTES, number=3) @@ -807,4 +884,25 @@ class FulfillIntentResponse(proto.Message): ) +class SentimentAnalysisResult(proto.Message): + r"""The result of sentiment analysis. Sentiment analysis inspects + user input and identifies the prevailing subjective opinion, + especially to determine a user's attitude as positive, negative, + or neutral. + + Attributes: + score (float): + Sentiment score between -1.0 (negative + sentiment) and 1.0 (positive sentiment). + magnitude (float): + A non-negative number in the [0, +inf) range, which + represents the absolute magnitude of sentiment, regardless + of score (positive or negative). + """ + + score = proto.Field(proto.FLOAT, number=1) + + magnitude = proto.Field(proto.FLOAT, number=2) + + __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflowcx_v3beta1/types/session_entity_type.py b/google/cloud/dialogflowcx_v3beta1/types/session_entity_type.py index e9448aac..b3f7da3a 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/session_entity_type.py +++ b/google/cloud/dialogflowcx_v3beta1/types/session_entity_type.py @@ -134,7 +134,7 @@ def raw_page(self): return self session_entity_types = proto.RepeatedField( - proto.MESSAGE, number=1, message=SessionEntityType, + proto.MESSAGE, number=1, message="SessionEntityType", ) next_page_token = proto.Field(proto.STRING, number=2) @@ -177,7 +177,7 @@ class CreateSessionEntityTypeRequest(proto.Message): parent = proto.Field(proto.STRING, number=1) session_entity_type = proto.Field( - proto.MESSAGE, number=2, message=SessionEntityType, + proto.MESSAGE, number=2, message="SessionEntityType", ) @@ -198,7 +198,7 @@ class UpdateSessionEntityTypeRequest(proto.Message): """ session_entity_type = proto.Field( - proto.MESSAGE, number=1, message=SessionEntityType, + proto.MESSAGE, number=1, message="SessionEntityType", ) update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) diff --git a/google/cloud/dialogflowcx_v3beta1/types/transition_route_group.py b/google/cloud/dialogflowcx_v3beta1/types/transition_route_group.py index 274c85a2..ec2ab584 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/transition_route_group.py +++ b/google/cloud/dialogflowcx_v3beta1/types/transition_route_group.py @@ -133,7 +133,7 @@ def raw_page(self): return self transition_route_groups = proto.RepeatedField( - proto.MESSAGE, number=1, message=TransitionRouteGroup, + proto.MESSAGE, number=1, message="TransitionRouteGroup", ) next_page_token = proto.Field(proto.STRING, number=2) @@ -198,7 +198,7 @@ class CreateTransitionRouteGroupRequest(proto.Message): parent = proto.Field(proto.STRING, number=1) transition_route_group = proto.Field( - proto.MESSAGE, number=2, message=TransitionRouteGroup, + proto.MESSAGE, number=2, message="TransitionRouteGroup", ) language_code = proto.Field(proto.STRING, number=3) @@ -229,7 +229,7 @@ class UpdateTransitionRouteGroupRequest(proto.Message): """ transition_route_group = proto.Field( - proto.MESSAGE, number=1, message=TransitionRouteGroup, + proto.MESSAGE, number=1, message="TransitionRouteGroup", ) update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) diff --git a/google/cloud/dialogflowcx_v3beta1/types/version.py b/google/cloud/dialogflowcx_v3beta1/types/version.py index b1674e22..57c63e8e 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/version.py +++ b/google/cloud/dialogflowcx_v3beta1/types/version.py @@ -145,7 +145,7 @@ class ListVersionsResponse(proto.Message): def raw_page(self): return self - versions = proto.RepeatedField(proto.MESSAGE, number=1, message=Version,) + versions = proto.RepeatedField(proto.MESSAGE, number=1, message="Version",) next_page_token = proto.Field(proto.STRING, number=2) @@ -182,7 +182,7 @@ class CreateVersionRequest(proto.Message): parent = proto.Field(proto.STRING, number=1) - version = proto.Field(proto.MESSAGE, number=2, message=Version,) + version = proto.Field(proto.MESSAGE, number=2, message="Version",) class UpdateVersionRequest(proto.Message): @@ -198,7 +198,7 @@ class UpdateVersionRequest(proto.Message): updated. """ - version = proto.Field(proto.MESSAGE, number=1, message=Version,) + version = proto.Field(proto.MESSAGE, number=1, message="Version",) update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) diff --git a/google/cloud/dialogflowcx_v3beta1/types/webhook.py b/google/cloud/dialogflowcx_v3beta1/types/webhook.py index d697c1f6..303a6441 100644 --- a/google/cloud/dialogflowcx_v3beta1/types/webhook.py +++ b/google/cloud/dialogflowcx_v3beta1/types/webhook.py @@ -150,7 +150,7 @@ class ListWebhooksResponse(proto.Message): def raw_page(self): return self - webhooks = proto.RepeatedField(proto.MESSAGE, number=1, message=Webhook,) + webhooks = proto.RepeatedField(proto.MESSAGE, number=1, message="Webhook",) next_page_token = proto.Field(proto.STRING, number=2) @@ -182,7 +182,7 @@ class CreateWebhookRequest(proto.Message): parent = proto.Field(proto.STRING, number=1) - webhook = proto.Field(proto.MESSAGE, number=2, message=Webhook,) + webhook = proto.Field(proto.MESSAGE, number=2, message="Webhook",) class UpdateWebhookRequest(proto.Message): @@ -198,7 +198,7 @@ class UpdateWebhookRequest(proto.Message): updated. """ - webhook = proto.Field(proto.MESSAGE, number=1, message=Webhook,) + webhook = proto.Field(proto.MESSAGE, number=1, message="Webhook",) update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) diff --git a/noxfile.py b/noxfile.py index 9350adb0..87765339 100644 --- a/noxfile.py +++ b/noxfile.py @@ -81,9 +81,8 @@ def default(session): session.run( "py.test", "--quiet", - "--cov=google.cloud.dialogflow-cx", - "--cov=google.cloud", - "--cov=tests.unit", + "--cov=google/cloud", + "--cov=tests/unit", "--cov-append", "--cov-config=.coveragerc", "--cov-report=", diff --git a/synth.metadata b/synth.metadata index a56b3428..119ec986 100644 --- a/synth.metadata +++ b/synth.metadata @@ -3,30 +3,22 @@ { "git": { "name": ".", - "remote": "https://github.com/googleapis/python-dialogflow-cx.git", - "sha": "e691f77b8415a5df684a67a1df4b1a1bddf6dace" - } - }, - { - "git": { - "name": "googleapis", - "remote": "https://github.com/googleapis/googleapis.git", - "sha": "b267d696cd58bc658b4e4b6cd698b05b5b6a073e", - "internalRef": "328987468" + "remote": "git@github.com:googleapis/python-dialogflow-cx", + "sha": "58c92adeaecfe333cb46aa4ff42bb703436e50f3" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "6542bd723403513626f61642fc02ddca528409aa" + "sha": "5d11bd2888c38ce1fb6fa6bf25494a4219a73928" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "6542bd723403513626f61642fc02ddca528409aa" + "sha": "5d11bd2888c38ce1fb6fa6bf25494a4219a73928" } } ], @@ -39,195 +31,15 @@ "language": "python", "generator": "bazel" } + }, + { + "client": { + "source": "googleapis", + "apiName": "dialogflow", + "apiVersion": "v3", + "language": "python", + "generator": "bazel" + } } - ], - "generatedFiles": [ - ".flake8", - ".github/CONTRIBUTING.md", - ".github/ISSUE_TEMPLATE/bug_report.md", - ".github/ISSUE_TEMPLATE/feature_request.md", - ".github/ISSUE_TEMPLATE/support_request.md", - ".github/PULL_REQUEST_TEMPLATE.md", - ".github/release-please.yml", - ".github/snippet-bot.yml", - ".gitignore", - ".kokoro/build.sh", - ".kokoro/continuous/common.cfg", - ".kokoro/continuous/continuous.cfg", - ".kokoro/docker/docs/Dockerfile", - ".kokoro/docker/docs/fetch_gpg_keys.sh", - ".kokoro/docs/common.cfg", - ".kokoro/docs/docs-presubmit.cfg", - ".kokoro/docs/docs.cfg", - ".kokoro/populate-secrets.sh", - ".kokoro/presubmit/common.cfg", - ".kokoro/presubmit/presubmit.cfg", - ".kokoro/publish-docs.sh", - ".kokoro/release.sh", - ".kokoro/release/common.cfg", - ".kokoro/release/release.cfg", - ".kokoro/samples/lint/common.cfg", - ".kokoro/samples/lint/continuous.cfg", - ".kokoro/samples/lint/periodic.cfg", - ".kokoro/samples/lint/presubmit.cfg", - ".kokoro/samples/python3.6/common.cfg", - ".kokoro/samples/python3.6/continuous.cfg", - ".kokoro/samples/python3.6/periodic.cfg", - ".kokoro/samples/python3.6/presubmit.cfg", - ".kokoro/samples/python3.7/common.cfg", - ".kokoro/samples/python3.7/continuous.cfg", - ".kokoro/samples/python3.7/periodic.cfg", - ".kokoro/samples/python3.7/presubmit.cfg", - ".kokoro/samples/python3.8/common.cfg", - ".kokoro/samples/python3.8/continuous.cfg", - ".kokoro/samples/python3.8/periodic.cfg", - ".kokoro/samples/python3.8/presubmit.cfg", - ".kokoro/test-samples.sh", - ".kokoro/trampoline.sh", - ".kokoro/trampoline_v2.sh", - ".trampolinerc", - "CODE_OF_CONDUCT.md", - "CONTRIBUTING.rst", - "LICENSE", - "MANIFEST.in", - "docs/_static/custom.css", - "docs/_templates/layout.html", - "docs/conf.py", - "docs/dialogflowcx_v3beta1/services.rst", - "docs/dialogflowcx_v3beta1/types.rst", - "docs/multiprocessing.rst", - "google/cloud/dialogflowcx/__init__.py", - "google/cloud/dialogflowcx/py.typed", - "google/cloud/dialogflowcx_v3beta1/__init__.py", - "google/cloud/dialogflowcx_v3beta1/py.typed", - "google/cloud/dialogflowcx_v3beta1/services/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/agents/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/agents/async_client.py", - "google/cloud/dialogflowcx_v3beta1/services/agents/client.py", - "google/cloud/dialogflowcx_v3beta1/services/agents/pagers.py", - "google/cloud/dialogflowcx_v3beta1/services/agents/transports/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/agents/transports/base.py", - "google/cloud/dialogflowcx_v3beta1/services/agents/transports/grpc.py", - "google/cloud/dialogflowcx_v3beta1/services/agents/transports/grpc_asyncio.py", - "google/cloud/dialogflowcx_v3beta1/services/entity_types/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/entity_types/async_client.py", - "google/cloud/dialogflowcx_v3beta1/services/entity_types/client.py", - "google/cloud/dialogflowcx_v3beta1/services/entity_types/pagers.py", - "google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/base.py", - "google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/grpc.py", - "google/cloud/dialogflowcx_v3beta1/services/entity_types/transports/grpc_asyncio.py", - "google/cloud/dialogflowcx_v3beta1/services/environments/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/environments/async_client.py", - "google/cloud/dialogflowcx_v3beta1/services/environments/client.py", - "google/cloud/dialogflowcx_v3beta1/services/environments/pagers.py", - "google/cloud/dialogflowcx_v3beta1/services/environments/transports/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/environments/transports/base.py", - "google/cloud/dialogflowcx_v3beta1/services/environments/transports/grpc.py", - "google/cloud/dialogflowcx_v3beta1/services/environments/transports/grpc_asyncio.py", - "google/cloud/dialogflowcx_v3beta1/services/flows/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/flows/async_client.py", - "google/cloud/dialogflowcx_v3beta1/services/flows/client.py", - "google/cloud/dialogflowcx_v3beta1/services/flows/pagers.py", - "google/cloud/dialogflowcx_v3beta1/services/flows/transports/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/flows/transports/base.py", - "google/cloud/dialogflowcx_v3beta1/services/flows/transports/grpc.py", - "google/cloud/dialogflowcx_v3beta1/services/flows/transports/grpc_asyncio.py", - "google/cloud/dialogflowcx_v3beta1/services/intents/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/intents/async_client.py", - "google/cloud/dialogflowcx_v3beta1/services/intents/client.py", - "google/cloud/dialogflowcx_v3beta1/services/intents/pagers.py", - "google/cloud/dialogflowcx_v3beta1/services/intents/transports/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/intents/transports/base.py", - "google/cloud/dialogflowcx_v3beta1/services/intents/transports/grpc.py", - "google/cloud/dialogflowcx_v3beta1/services/intents/transports/grpc_asyncio.py", - "google/cloud/dialogflowcx_v3beta1/services/pages/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/pages/async_client.py", - "google/cloud/dialogflowcx_v3beta1/services/pages/client.py", - "google/cloud/dialogflowcx_v3beta1/services/pages/pagers.py", - "google/cloud/dialogflowcx_v3beta1/services/pages/transports/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/pages/transports/base.py", - "google/cloud/dialogflowcx_v3beta1/services/pages/transports/grpc.py", - "google/cloud/dialogflowcx_v3beta1/services/pages/transports/grpc_asyncio.py", - "google/cloud/dialogflowcx_v3beta1/services/session_entity_types/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/session_entity_types/async_client.py", - "google/cloud/dialogflowcx_v3beta1/services/session_entity_types/client.py", - "google/cloud/dialogflowcx_v3beta1/services/session_entity_types/pagers.py", - "google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/base.py", - "google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/grpc.py", - "google/cloud/dialogflowcx_v3beta1/services/session_entity_types/transports/grpc_asyncio.py", - "google/cloud/dialogflowcx_v3beta1/services/sessions/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/sessions/async_client.py", - "google/cloud/dialogflowcx_v3beta1/services/sessions/client.py", - "google/cloud/dialogflowcx_v3beta1/services/sessions/transports/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/sessions/transports/base.py", - "google/cloud/dialogflowcx_v3beta1/services/sessions/transports/grpc.py", - "google/cloud/dialogflowcx_v3beta1/services/sessions/transports/grpc_asyncio.py", - "google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/async_client.py", - "google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/client.py", - "google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/pagers.py", - "google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/base.py", - "google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/grpc.py", - "google/cloud/dialogflowcx_v3beta1/services/transition_route_groups/transports/grpc_asyncio.py", - "google/cloud/dialogflowcx_v3beta1/services/versions/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/versions/async_client.py", - "google/cloud/dialogflowcx_v3beta1/services/versions/client.py", - "google/cloud/dialogflowcx_v3beta1/services/versions/pagers.py", - "google/cloud/dialogflowcx_v3beta1/services/versions/transports/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/versions/transports/base.py", - "google/cloud/dialogflowcx_v3beta1/services/versions/transports/grpc.py", - "google/cloud/dialogflowcx_v3beta1/services/versions/transports/grpc_asyncio.py", - "google/cloud/dialogflowcx_v3beta1/services/webhooks/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/webhooks/async_client.py", - "google/cloud/dialogflowcx_v3beta1/services/webhooks/client.py", - "google/cloud/dialogflowcx_v3beta1/services/webhooks/pagers.py", - "google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/__init__.py", - "google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/base.py", - "google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/grpc.py", - "google/cloud/dialogflowcx_v3beta1/services/webhooks/transports/grpc_asyncio.py", - "google/cloud/dialogflowcx_v3beta1/types/__init__.py", - "google/cloud/dialogflowcx_v3beta1/types/agent.py", - "google/cloud/dialogflowcx_v3beta1/types/audio_config.py", - "google/cloud/dialogflowcx_v3beta1/types/entity_type.py", - "google/cloud/dialogflowcx_v3beta1/types/environment.py", - "google/cloud/dialogflowcx_v3beta1/types/flow.py", - "google/cloud/dialogflowcx_v3beta1/types/fulfillment.py", - "google/cloud/dialogflowcx_v3beta1/types/gcs.py", - "google/cloud/dialogflowcx_v3beta1/types/intent.py", - "google/cloud/dialogflowcx_v3beta1/types/ivr.py", - "google/cloud/dialogflowcx_v3beta1/types/page.py", - "google/cloud/dialogflowcx_v3beta1/types/response_message.py", - "google/cloud/dialogflowcx_v3beta1/types/session.py", - "google/cloud/dialogflowcx_v3beta1/types/session_entity_type.py", - "google/cloud/dialogflowcx_v3beta1/types/transition_route_group.py", - "google/cloud/dialogflowcx_v3beta1/types/version.py", - "google/cloud/dialogflowcx_v3beta1/types/webhook.py", - "mypy.ini", - "noxfile.py", - "renovate.json", - "scripts/decrypt-secrets.sh", - "scripts/readme-gen/readme_gen.py", - "scripts/readme-gen/templates/README.tmpl.rst", - "scripts/readme-gen/templates/auth.tmpl.rst", - "scripts/readme-gen/templates/auth_api_key.tmpl.rst", - "scripts/readme-gen/templates/install_deps.tmpl.rst", - "scripts/readme-gen/templates/install_portaudio.tmpl.rst", - "setup.cfg", - "testing/.gitignore", - "tests/unit/gapic/dialogflowcx_v3beta1/__init__.py", - "tests/unit/gapic/dialogflowcx_v3beta1/test_agents.py", - "tests/unit/gapic/dialogflowcx_v3beta1/test_entity_types.py", - "tests/unit/gapic/dialogflowcx_v3beta1/test_environments.py", - "tests/unit/gapic/dialogflowcx_v3beta1/test_flows.py", - "tests/unit/gapic/dialogflowcx_v3beta1/test_intents.py", - "tests/unit/gapic/dialogflowcx_v3beta1/test_pages.py", - "tests/unit/gapic/dialogflowcx_v3beta1/test_session_entity_types.py", - "tests/unit/gapic/dialogflowcx_v3beta1/test_sessions.py", - "tests/unit/gapic/dialogflowcx_v3beta1/test_transition_route_groups.py", - "tests/unit/gapic/dialogflowcx_v3beta1/test_versions.py", - "tests/unit/gapic/dialogflowcx_v3beta1/test_webhooks.py" ] } \ No newline at end of file diff --git a/synth.py b/synth.py index 37f3d5c3..753ad349 100644 --- a/synth.py +++ b/synth.py @@ -25,22 +25,25 @@ # ---------------------------------------------------------------------------- # Generate dialogflow CX GAPIC layer # ---------------------------------------------------------------------------- -library = gapic.py_library( - service="dialogflow", - version="v3beta1", - bazel_target="//google/cloud/dialogflow/cx/v3beta1:dialogflow-cx-v3beta1-py", - -) - -s.move( - library, - excludes=[ - "setup.py", - "docs/index.rst", - "noxfile.py", - "scripts/fixup_dialogflowcx_v3beta1_keywords.py", - ], -) +versions = ["v3beta1", "v3"] + +for version in versions: + library = gapic.py_library( + service="dialogflow", + version=version, + bazel_target=f"//google/cloud/dialogflow/cx/{version}:dialogflow-cx-{version}-py", + + ) + + s.move( + library, + excludes=[ + "setup.py", + "docs/index.rst", + "noxfile.py", + f"scripts/fixup_dialogflowcx_{version}_keywords.py", + ], + ) # ---------------------------------------------------------------------------- # Add templated files diff --git a/tests/unit/gapic/dialogflowcx_v3/__init__.py b/tests/unit/gapic/dialogflowcx_v3/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/__init__.py @@ -0,0 +1 @@ + diff --git a/tests/unit/gapic/dialogflowcx_v3/test_agents.py b/tests/unit/gapic/dialogflowcx_v3/test_agents.py new file mode 100644 index 00000000..6ae40d30 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_agents.py @@ -0,0 +1,2368 @@ +# -*- 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 future +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.api_core import operation_async # type: ignore +from google.api_core import operations_v1 +from google.auth import credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.dialogflowcx_v3.services.agents import AgentsAsyncClient +from google.cloud.dialogflowcx_v3.services.agents import AgentsClient +from google.cloud.dialogflowcx_v3.services.agents import pagers +from google.cloud.dialogflowcx_v3.services.agents import transports +from google.cloud.dialogflowcx_v3.types import agent +from google.cloud.dialogflowcx_v3.types import agent as gcdc_agent +from google.longrunning import operations_pb2 +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # 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 AgentsClient._get_default_mtls_endpoint(None) is None + assert AgentsClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + AgentsClient._get_default_mtls_endpoint(api_mtls_endpoint) == api_mtls_endpoint + ) + assert ( + AgentsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + AgentsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert AgentsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [AgentsClient, AgentsAsyncClient]) +def test_agents_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 == "dialogflow.googleapis.com:443" + + +def test_agents_client_get_transport_class(): + transport = AgentsClient.get_transport_class() + assert transport == transports.AgentsGrpcTransport + + transport = AgentsClient.get_transport_class("grpc") + assert transport == transports.AgentsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (AgentsClient, transports.AgentsGrpcTransport, "grpc"), + (AgentsAsyncClient, transports.AgentsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +@mock.patch.object( + AgentsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(AgentsClient) +) +@mock.patch.object( + AgentsAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(AgentsAsyncClient) +) +def test_agents_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(AgentsClient, "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(AgentsClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + (AgentsClient, transports.AgentsGrpcTransport, "grpc", "true"), + ( + AgentsAsyncClient, + transports.AgentsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (AgentsClient, transports.AgentsGrpcTransport, "grpc", "false"), + ( + AgentsAsyncClient, + transports.AgentsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + AgentsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(AgentsClient) +) +@mock.patch.object( + AgentsAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(AgentsAsyncClient) +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_agents_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (AgentsClient, transports.AgentsGrpcTransport, "grpc"), + (AgentsAsyncClient, transports.AgentsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_agents_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (AgentsClient, transports.AgentsGrpcTransport, "grpc"), + (AgentsAsyncClient, transports.AgentsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_agents_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_agents_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.agents.transports.AgentsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = AgentsClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_agents(transport: str = "grpc", request_type=agent.ListAgentsRequest): + client = AgentsClient( + 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_agents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = agent.ListAgentsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_agents(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.ListAgentsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListAgentsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_agents_from_dict(): + test_list_agents(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_agents_async( + transport: str = "grpc_asyncio", request_type=agent.ListAgentsRequest +): + client = AgentsAsyncClient( + 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_agents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + agent.ListAgentsResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_agents(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.ListAgentsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListAgentsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_agents_async_from_dict(): + await test_list_agents_async(request_type=dict) + + +def test_list_agents_field_headers(): + client = AgentsClient(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 = agent.ListAgentsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: + call.return_value = agent.ListAgentsResponse() + + client.list_agents(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_agents_field_headers_async(): + client = AgentsAsyncClient(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 = agent.ListAgentsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + agent.ListAgentsResponse() + ) + + await client.list_agents(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_agents_flattened(): + client = AgentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = agent.ListAgentsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_agents(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_agents_flattened_error(): + client = AgentsClient(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_agents( + agent.ListAgentsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_agents_flattened_async(): + client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = agent.ListAgentsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + agent.ListAgentsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_agents(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_agents_flattened_error_async(): + client = AgentsAsyncClient(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_agents( + agent.ListAgentsRequest(), parent="parent_value", + ) + + +def test_list_agents_pager(): + client = AgentsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + agent.ListAgentsResponse( + agents=[agent.Agent(), agent.Agent(), agent.Agent(),], + next_page_token="abc", + ), + agent.ListAgentsResponse(agents=[], next_page_token="def",), + agent.ListAgentsResponse(agents=[agent.Agent(),], next_page_token="ghi",), + agent.ListAgentsResponse(agents=[agent.Agent(), agent.Agent(),],), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_agents(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, agent.Agent) for i in results) + + +def test_list_agents_pages(): + client = AgentsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + agent.ListAgentsResponse( + agents=[agent.Agent(), agent.Agent(), agent.Agent(),], + next_page_token="abc", + ), + agent.ListAgentsResponse(agents=[], next_page_token="def",), + agent.ListAgentsResponse(agents=[agent.Agent(),], next_page_token="ghi",), + agent.ListAgentsResponse(agents=[agent.Agent(), agent.Agent(),],), + RuntimeError, + ) + pages = list(client.list_agents(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_agents_async_pager(): + client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_agents), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + agent.ListAgentsResponse( + agents=[agent.Agent(), agent.Agent(), agent.Agent(),], + next_page_token="abc", + ), + agent.ListAgentsResponse(agents=[], next_page_token="def",), + agent.ListAgentsResponse(agents=[agent.Agent(),], next_page_token="ghi",), + agent.ListAgentsResponse(agents=[agent.Agent(), agent.Agent(),],), + RuntimeError, + ) + async_pager = await client.list_agents(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, agent.Agent) for i in responses) + + +@pytest.mark.asyncio +async def test_list_agents_async_pages(): + client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_agents), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + agent.ListAgentsResponse( + agents=[agent.Agent(), agent.Agent(), agent.Agent(),], + next_page_token="abc", + ), + agent.ListAgentsResponse(agents=[], next_page_token="def",), + agent.ListAgentsResponse(agents=[agent.Agent(),], next_page_token="ghi",), + agent.ListAgentsResponse(agents=[agent.Agent(), agent.Agent(),],), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_agents(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_agent(transport: str = "grpc", request_type=agent.GetAgentRequest): + client = AgentsClient( + 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_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = agent.Agent( + name="name_value", + display_name="display_name_value", + default_language_code="default_language_code_value", + time_zone="time_zone_value", + description="description_value", + avatar_uri="avatar_uri_value", + start_flow="start_flow_value", + enable_stackdriver_logging=True, + enable_spell_correction=True, + ) + + response = client.get_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.GetAgentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, agent.Agent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.default_language_code == "default_language_code_value" + + assert response.time_zone == "time_zone_value" + + assert response.description == "description_value" + + assert response.avatar_uri == "avatar_uri_value" + + assert response.start_flow == "start_flow_value" + + assert response.enable_stackdriver_logging is True + + assert response.enable_spell_correction is True + + +def test_get_agent_from_dict(): + test_get_agent(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_agent_async( + transport: str = "grpc_asyncio", request_type=agent.GetAgentRequest +): + client = AgentsAsyncClient( + 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_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + agent.Agent( + name="name_value", + display_name="display_name_value", + default_language_code="default_language_code_value", + time_zone="time_zone_value", + description="description_value", + avatar_uri="avatar_uri_value", + start_flow="start_flow_value", + enable_stackdriver_logging=True, + enable_spell_correction=True, + ) + ) + + response = await client.get_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.GetAgentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, agent.Agent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.default_language_code == "default_language_code_value" + + assert response.time_zone == "time_zone_value" + + assert response.description == "description_value" + + assert response.avatar_uri == "avatar_uri_value" + + assert response.start_flow == "start_flow_value" + + assert response.enable_stackdriver_logging is True + + assert response.enable_spell_correction is True + + +@pytest.mark.asyncio +async def test_get_agent_async_from_dict(): + await test_get_agent_async(request_type=dict) + + +def test_get_agent_field_headers(): + client = AgentsClient(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 = agent.GetAgentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: + call.return_value = agent.Agent() + + client.get_agent(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_agent_field_headers_async(): + client = AgentsAsyncClient(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 = agent.GetAgentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(agent.Agent()) + + await client.get_agent(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_agent_flattened(): + client = AgentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = agent.Agent() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_agent(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_agent_flattened_error(): + client = AgentsClient(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_agent( + agent.GetAgentRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_agent_flattened_async(): + client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = agent.Agent() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(agent.Agent()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_agent(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_agent_flattened_error_async(): + client = AgentsAsyncClient(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_agent( + agent.GetAgentRequest(), name="name_value", + ) + + +def test_create_agent( + transport: str = "grpc", request_type=gcdc_agent.CreateAgentRequest +): + client = AgentsClient( + 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_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_agent.Agent( + name="name_value", + display_name="display_name_value", + default_language_code="default_language_code_value", + time_zone="time_zone_value", + description="description_value", + avatar_uri="avatar_uri_value", + start_flow="start_flow_value", + enable_stackdriver_logging=True, + enable_spell_correction=True, + ) + + response = client.create_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_agent.CreateAgentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_agent.Agent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.default_language_code == "default_language_code_value" + + assert response.time_zone == "time_zone_value" + + assert response.description == "description_value" + + assert response.avatar_uri == "avatar_uri_value" + + assert response.start_flow == "start_flow_value" + + assert response.enable_stackdriver_logging is True + + assert response.enable_spell_correction is True + + +def test_create_agent_from_dict(): + test_create_agent(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_agent_async( + transport: str = "grpc_asyncio", request_type=gcdc_agent.CreateAgentRequest +): + client = AgentsAsyncClient( + 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_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_agent.Agent( + name="name_value", + display_name="display_name_value", + default_language_code="default_language_code_value", + time_zone="time_zone_value", + description="description_value", + avatar_uri="avatar_uri_value", + start_flow="start_flow_value", + enable_stackdriver_logging=True, + enable_spell_correction=True, + ) + ) + + response = await client.create_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_agent.CreateAgentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_agent.Agent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.default_language_code == "default_language_code_value" + + assert response.time_zone == "time_zone_value" + + assert response.description == "description_value" + + assert response.avatar_uri == "avatar_uri_value" + + assert response.start_flow == "start_flow_value" + + assert response.enable_stackdriver_logging is True + + assert response.enable_spell_correction is True + + +@pytest.mark.asyncio +async def test_create_agent_async_from_dict(): + await test_create_agent_async(request_type=dict) + + +def test_create_agent_field_headers(): + client = AgentsClient(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 = gcdc_agent.CreateAgentRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_agent), "__call__") as call: + call.return_value = gcdc_agent.Agent() + + client.create_agent(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_agent_field_headers_async(): + client = AgentsAsyncClient(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 = gcdc_agent.CreateAgentRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_agent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_agent.Agent()) + + await client.create_agent(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_agent_flattened(): + client = AgentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_agent.Agent() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_agent( + parent="parent_value", agent=gcdc_agent.Agent(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].agent == gcdc_agent.Agent(name="name_value") + + +def test_create_agent_flattened_error(): + client = AgentsClient(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_agent( + gcdc_agent.CreateAgentRequest(), + parent="parent_value", + agent=gcdc_agent.Agent(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_agent_flattened_async(): + client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_agent.Agent() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_agent.Agent()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_agent( + parent="parent_value", agent=gcdc_agent.Agent(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].agent == gcdc_agent.Agent(name="name_value") + + +@pytest.mark.asyncio +async def test_create_agent_flattened_error_async(): + client = AgentsAsyncClient(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_agent( + gcdc_agent.CreateAgentRequest(), + parent="parent_value", + agent=gcdc_agent.Agent(name="name_value"), + ) + + +def test_update_agent( + transport: str = "grpc", request_type=gcdc_agent.UpdateAgentRequest +): + client = AgentsClient( + 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_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_agent.Agent( + name="name_value", + display_name="display_name_value", + default_language_code="default_language_code_value", + time_zone="time_zone_value", + description="description_value", + avatar_uri="avatar_uri_value", + start_flow="start_flow_value", + enable_stackdriver_logging=True, + enable_spell_correction=True, + ) + + response = client.update_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_agent.UpdateAgentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_agent.Agent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.default_language_code == "default_language_code_value" + + assert response.time_zone == "time_zone_value" + + assert response.description == "description_value" + + assert response.avatar_uri == "avatar_uri_value" + + assert response.start_flow == "start_flow_value" + + assert response.enable_stackdriver_logging is True + + assert response.enable_spell_correction is True + + +def test_update_agent_from_dict(): + test_update_agent(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_agent_async( + transport: str = "grpc_asyncio", request_type=gcdc_agent.UpdateAgentRequest +): + client = AgentsAsyncClient( + 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_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_agent.Agent( + name="name_value", + display_name="display_name_value", + default_language_code="default_language_code_value", + time_zone="time_zone_value", + description="description_value", + avatar_uri="avatar_uri_value", + start_flow="start_flow_value", + enable_stackdriver_logging=True, + enable_spell_correction=True, + ) + ) + + response = await client.update_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_agent.UpdateAgentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_agent.Agent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.default_language_code == "default_language_code_value" + + assert response.time_zone == "time_zone_value" + + assert response.description == "description_value" + + assert response.avatar_uri == "avatar_uri_value" + + assert response.start_flow == "start_flow_value" + + assert response.enable_stackdriver_logging is True + + assert response.enable_spell_correction is True + + +@pytest.mark.asyncio +async def test_update_agent_async_from_dict(): + await test_update_agent_async(request_type=dict) + + +def test_update_agent_field_headers(): + client = AgentsClient(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 = gcdc_agent.UpdateAgentRequest() + request.agent.name = "agent.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_agent), "__call__") as call: + call.return_value = gcdc_agent.Agent() + + client.update_agent(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", "agent.name=agent.name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_agent_field_headers_async(): + client = AgentsAsyncClient(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 = gcdc_agent.UpdateAgentRequest() + request.agent.name = "agent.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_agent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_agent.Agent()) + + await client.update_agent(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", "agent.name=agent.name/value",) in kw["metadata"] + + +def test_update_agent_flattened(): + client = AgentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_agent.Agent() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_agent( + agent=gcdc_agent.Agent(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].agent == gcdc_agent.Agent(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_agent_flattened_error(): + client = AgentsClient(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_agent( + gcdc_agent.UpdateAgentRequest(), + agent=gcdc_agent.Agent(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_agent_flattened_async(): + client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_agent.Agent() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_agent.Agent()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_agent( + agent=gcdc_agent.Agent(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].agent == gcdc_agent.Agent(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_agent_flattened_error_async(): + client = AgentsAsyncClient(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_agent( + gcdc_agent.UpdateAgentRequest(), + agent=gcdc_agent.Agent(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_agent(transport: str = "grpc", request_type=agent.DeleteAgentRequest): + client = AgentsClient( + 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_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.DeleteAgentRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_agent_from_dict(): + test_delete_agent(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_agent_async( + transport: str = "grpc_asyncio", request_type=agent.DeleteAgentRequest +): + client = AgentsAsyncClient( + 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_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.DeleteAgentRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_agent_async_from_dict(): + await test_delete_agent_async(request_type=dict) + + +def test_delete_agent_field_headers(): + client = AgentsClient(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 = agent.DeleteAgentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_agent), "__call__") as call: + call.return_value = None + + client.delete_agent(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_agent_field_headers_async(): + client = AgentsAsyncClient(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 = agent.DeleteAgentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_agent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_agent(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_agent_flattened(): + client = AgentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_agent), "__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_agent(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_agent_flattened_error(): + client = AgentsClient(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_agent( + agent.DeleteAgentRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_agent_flattened_async(): + client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_agent), "__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_agent(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_agent_flattened_error_async(): + client = AgentsAsyncClient(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_agent( + agent.DeleteAgentRequest(), name="name_value", + ) + + +def test_export_agent(transport: str = "grpc", request_type=agent.ExportAgentRequest): + client = AgentsClient( + 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.export_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.export_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.ExportAgentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_export_agent_from_dict(): + test_export_agent(request_type=dict) + + +@pytest.mark.asyncio +async def test_export_agent_async( + transport: str = "grpc_asyncio", request_type=agent.ExportAgentRequest +): + client = AgentsAsyncClient( + 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.export_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + + response = await client.export_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.ExportAgentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_export_agent_async_from_dict(): + await test_export_agent_async(request_type=dict) + + +def test_export_agent_field_headers(): + client = AgentsClient(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 = agent.ExportAgentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.export_agent), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.export_agent(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_export_agent_field_headers_async(): + client = AgentsAsyncClient(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 = agent.ExportAgentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.export_agent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.export_agent(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_restore_agent(transport: str = "grpc", request_type=agent.RestoreAgentRequest): + client = AgentsClient( + 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.restore_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.restore_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.RestoreAgentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_restore_agent_from_dict(): + test_restore_agent(request_type=dict) + + +@pytest.mark.asyncio +async def test_restore_agent_async( + transport: str = "grpc_asyncio", request_type=agent.RestoreAgentRequest +): + client = AgentsAsyncClient( + 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.restore_agent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + + response = await client.restore_agent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.RestoreAgentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_restore_agent_async_from_dict(): + await test_restore_agent_async(request_type=dict) + + +def test_restore_agent_field_headers(): + client = AgentsClient(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 = agent.RestoreAgentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.restore_agent), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.restore_agent(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_restore_agent_field_headers_async(): + client = AgentsAsyncClient(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 = agent.RestoreAgentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.restore_agent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.restore_agent(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_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.AgentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.AgentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = AgentsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.AgentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = AgentsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.AgentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = AgentsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.AgentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.AgentsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], +) +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 = AgentsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.AgentsGrpcTransport,) + + +def test_agents_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.AgentsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_agents_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.agents.transports.AgentsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.AgentsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_agents", + "get_agent", + "create_agent", + "update_agent", + "delete_agent", + "export_agent", + "restore_agent", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + # Additionally, the LRO client (a property) should + # also raise NotImplementedError + with pytest.raises(NotImplementedError): + transport.operations_client + + +def test_agents_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.dialogflowcx_v3.services.agents.transports.AgentsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.AgentsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_agents_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.dialogflowcx_v3.services.agents.transports.AgentsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.AgentsTransport() + adc.assert_called_once() + + +def test_agents_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) + AgentsClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_agents_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.AgentsGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_agents_host_no_port(): + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_agents_host_with_port(): + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_agents_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.AgentsGrpcTransport( + 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_agents_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.AgentsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], +) +def test_agents_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], +) +def test_agents_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_agents_grpc_lro_client(): + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_agents_grpc_lro_async_client(): + client = AgentsAsyncClient( + credentials=credentials.AnonymousCredentials(), transport="grpc_asyncio", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_agent_path(): + project = "squid" + location = "clam" + agent = "whelk" + + expected = "projects/{project}/locations/{location}/agents/{agent}".format( + project=project, location=location, agent=agent, + ) + actual = AgentsClient.agent_path(project, location, agent) + assert expected == actual + + +def test_parse_agent_path(): + expected = { + "project": "octopus", + "location": "oyster", + "agent": "nudibranch", + } + path = AgentsClient.agent_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_agent_path(path) + assert expected == actual + + +def test_flow_path(): + project = "cuttlefish" + location = "mussel" + agent = "winkle" + flow = "nautilus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + actual = AgentsClient.flow_path(project, location, agent, flow) + assert expected == actual + + +def test_parse_flow_path(): + expected = { + "project": "scallop", + "location": "abalone", + "agent": "squid", + "flow": "clam", + } + path = AgentsClient.flow_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_flow_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "whelk" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = AgentsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "octopus", + } + path = AgentsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "oyster" + + expected = "folders/{folder}".format(folder=folder,) + actual = AgentsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nudibranch", + } + path = AgentsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "cuttlefish" + + expected = "organizations/{organization}".format(organization=organization,) + actual = AgentsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "mussel", + } + path = AgentsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "winkle" + + expected = "projects/{project}".format(project=project,) + actual = AgentsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nautilus", + } + path = AgentsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "scallop" + location = "abalone" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = AgentsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "squid", + "location": "clam", + } + path = AgentsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.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.AgentsTransport, "_prep_wrapped_messages" + ) as prep: + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.AgentsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = AgentsClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3/test_entity_types.py b/tests/unit/gapic/dialogflowcx_v3/test_entity_types.py new file mode 100644 index 00000000..c2dc6f59 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_entity_types.py @@ -0,0 +1,2162 @@ +# -*- 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.dialogflowcx_v3.services.entity_types import EntityTypesAsyncClient +from google.cloud.dialogflowcx_v3.services.entity_types import EntityTypesClient +from google.cloud.dialogflowcx_v3.services.entity_types import pagers +from google.cloud.dialogflowcx_v3.services.entity_types import transports +from google.cloud.dialogflowcx_v3.types import entity_type +from google.cloud.dialogflowcx_v3.types import entity_type as gcdc_entity_type +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # 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 EntityTypesClient._get_default_mtls_endpoint(None) is None + assert ( + EntityTypesClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + ) + assert ( + EntityTypesClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + EntityTypesClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + EntityTypesClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert EntityTypesClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [EntityTypesClient, EntityTypesAsyncClient]) +def test_entity_types_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 == "dialogflow.googleapis.com:443" + + +def test_entity_types_client_get_transport_class(): + transport = EntityTypesClient.get_transport_class() + assert transport == transports.EntityTypesGrpcTransport + + transport = EntityTypesClient.get_transport_class("grpc") + assert transport == transports.EntityTypesGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (EntityTypesClient, transports.EntityTypesGrpcTransport, "grpc"), + ( + EntityTypesAsyncClient, + transports.EntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + EntityTypesClient, "DEFAULT_ENDPOINT", modify_default_endpoint(EntityTypesClient) +) +@mock.patch.object( + EntityTypesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(EntityTypesAsyncClient), +) +def test_entity_types_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(EntityTypesClient, "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(EntityTypesClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + (EntityTypesClient, transports.EntityTypesGrpcTransport, "grpc", "true"), + ( + EntityTypesAsyncClient, + transports.EntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (EntityTypesClient, transports.EntityTypesGrpcTransport, "grpc", "false"), + ( + EntityTypesAsyncClient, + transports.EntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + EntityTypesClient, "DEFAULT_ENDPOINT", modify_default_endpoint(EntityTypesClient) +) +@mock.patch.object( + EntityTypesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(EntityTypesAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_entity_types_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (EntityTypesClient, transports.EntityTypesGrpcTransport, "grpc"), + ( + EntityTypesAsyncClient, + transports.EntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_entity_types_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (EntityTypesClient, transports.EntityTypesGrpcTransport, "grpc"), + ( + EntityTypesAsyncClient, + transports.EntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_entity_types_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_entity_types_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.entity_types.transports.EntityTypesGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = EntityTypesClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_entity_types( + transport: str = "grpc", request_type=entity_type.ListEntityTypesRequest +): + client = EntityTypesClient( + 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_entity_types), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = entity_type.ListEntityTypesResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_entity_types(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.ListEntityTypesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListEntityTypesPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_entity_types_from_dict(): + test_list_entity_types(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_entity_types_async( + transport: str = "grpc_asyncio", request_type=entity_type.ListEntityTypesRequest +): + client = EntityTypesAsyncClient( + 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_entity_types), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + entity_type.ListEntityTypesResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_entity_types(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.ListEntityTypesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListEntityTypesAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_entity_types_async_from_dict(): + await test_list_entity_types_async(request_type=dict) + + +def test_list_entity_types_field_headers(): + client = EntityTypesClient(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 = entity_type.ListEntityTypesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_entity_types), "__call__" + ) as call: + call.return_value = entity_type.ListEntityTypesResponse() + + client.list_entity_types(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_entity_types_field_headers_async(): + client = EntityTypesAsyncClient(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 = entity_type.ListEntityTypesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_entity_types), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + entity_type.ListEntityTypesResponse() + ) + + await client.list_entity_types(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_entity_types_flattened(): + client = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_entity_types), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = entity_type.ListEntityTypesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_entity_types(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_entity_types_flattened_error(): + client = EntityTypesClient(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_entity_types( + entity_type.ListEntityTypesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_entity_types_flattened_async(): + client = EntityTypesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_entity_types), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = entity_type.ListEntityTypesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + entity_type.ListEntityTypesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_entity_types(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_entity_types_flattened_error_async(): + client = EntityTypesAsyncClient(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_entity_types( + entity_type.ListEntityTypesRequest(), parent="parent_value", + ) + + +def test_list_entity_types_pager(): + client = EntityTypesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_entity_types), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + entity_type.ListEntityTypesResponse( + entity_types=[ + entity_type.EntityType(), + entity_type.EntityType(), + entity_type.EntityType(), + ], + next_page_token="abc", + ), + entity_type.ListEntityTypesResponse( + entity_types=[], next_page_token="def", + ), + entity_type.ListEntityTypesResponse( + entity_types=[entity_type.EntityType(),], next_page_token="ghi", + ), + entity_type.ListEntityTypesResponse( + entity_types=[entity_type.EntityType(), entity_type.EntityType(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_entity_types(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, entity_type.EntityType) for i in results) + + +def test_list_entity_types_pages(): + client = EntityTypesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_entity_types), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + entity_type.ListEntityTypesResponse( + entity_types=[ + entity_type.EntityType(), + entity_type.EntityType(), + entity_type.EntityType(), + ], + next_page_token="abc", + ), + entity_type.ListEntityTypesResponse( + entity_types=[], next_page_token="def", + ), + entity_type.ListEntityTypesResponse( + entity_types=[entity_type.EntityType(),], next_page_token="ghi", + ), + entity_type.ListEntityTypesResponse( + entity_types=[entity_type.EntityType(), entity_type.EntityType(),], + ), + RuntimeError, + ) + pages = list(client.list_entity_types(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_entity_types_async_pager(): + client = EntityTypesAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_entity_types), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + entity_type.ListEntityTypesResponse( + entity_types=[ + entity_type.EntityType(), + entity_type.EntityType(), + entity_type.EntityType(), + ], + next_page_token="abc", + ), + entity_type.ListEntityTypesResponse( + entity_types=[], next_page_token="def", + ), + entity_type.ListEntityTypesResponse( + entity_types=[entity_type.EntityType(),], next_page_token="ghi", + ), + entity_type.ListEntityTypesResponse( + entity_types=[entity_type.EntityType(), entity_type.EntityType(),], + ), + RuntimeError, + ) + async_pager = await client.list_entity_types(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, entity_type.EntityType) for i in responses) + + +@pytest.mark.asyncio +async def test_list_entity_types_async_pages(): + client = EntityTypesAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_entity_types), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + entity_type.ListEntityTypesResponse( + entity_types=[ + entity_type.EntityType(), + entity_type.EntityType(), + entity_type.EntityType(), + ], + next_page_token="abc", + ), + entity_type.ListEntityTypesResponse( + entity_types=[], next_page_token="def", + ), + entity_type.ListEntityTypesResponse( + entity_types=[entity_type.EntityType(),], next_page_token="ghi", + ), + entity_type.ListEntityTypesResponse( + entity_types=[entity_type.EntityType(), entity_type.EntityType(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_entity_types(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_entity_type( + transport: str = "grpc", request_type=entity_type.GetEntityTypeRequest +): + client = EntityTypesClient( + 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_entity_type), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = entity_type.EntityType( + name="name_value", + display_name="display_name_value", + kind=entity_type.EntityType.Kind.KIND_MAP, + auto_expansion_mode=entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT, + enable_fuzzy_extraction=True, + ) + + response = client.get_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.GetEntityTypeRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, entity_type.EntityType) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.kind == entity_type.EntityType.Kind.KIND_MAP + + assert ( + response.auto_expansion_mode + == entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT + ) + + assert response.enable_fuzzy_extraction is True + + +def test_get_entity_type_from_dict(): + test_get_entity_type(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_entity_type_async( + transport: str = "grpc_asyncio", request_type=entity_type.GetEntityTypeRequest +): + client = EntityTypesAsyncClient( + 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_entity_type), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + entity_type.EntityType( + name="name_value", + display_name="display_name_value", + kind=entity_type.EntityType.Kind.KIND_MAP, + auto_expansion_mode=entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT, + enable_fuzzy_extraction=True, + ) + ) + + response = await client.get_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.GetEntityTypeRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, entity_type.EntityType) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.kind == entity_type.EntityType.Kind.KIND_MAP + + assert ( + response.auto_expansion_mode + == entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT + ) + + assert response.enable_fuzzy_extraction is True + + +@pytest.mark.asyncio +async def test_get_entity_type_async_from_dict(): + await test_get_entity_type_async(request_type=dict) + + +def test_get_entity_type_field_headers(): + client = EntityTypesClient(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 = entity_type.GetEntityTypeRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: + call.return_value = entity_type.EntityType() + + client.get_entity_type(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_entity_type_field_headers_async(): + client = EntityTypesAsyncClient(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 = entity_type.GetEntityTypeRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + entity_type.EntityType() + ) + + await client.get_entity_type(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_entity_type_flattened(): + client = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = entity_type.EntityType() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_entity_type(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_entity_type_flattened_error(): + client = EntityTypesClient(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_entity_type( + entity_type.GetEntityTypeRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_entity_type_flattened_async(): + client = EntityTypesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = entity_type.EntityType() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + entity_type.EntityType() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_entity_type(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_entity_type_flattened_error_async(): + client = EntityTypesAsyncClient(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_entity_type( + entity_type.GetEntityTypeRequest(), name="name_value", + ) + + +def test_create_entity_type( + transport: str = "grpc", request_type=gcdc_entity_type.CreateEntityTypeRequest +): + client = EntityTypesClient( + 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_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_entity_type.EntityType( + name="name_value", + display_name="display_name_value", + kind=gcdc_entity_type.EntityType.Kind.KIND_MAP, + auto_expansion_mode=gcdc_entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT, + enable_fuzzy_extraction=True, + ) + + response = client.create_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_entity_type.CreateEntityTypeRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_entity_type.EntityType) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.kind == gcdc_entity_type.EntityType.Kind.KIND_MAP + + assert ( + response.auto_expansion_mode + == gcdc_entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT + ) + + assert response.enable_fuzzy_extraction is True + + +def test_create_entity_type_from_dict(): + test_create_entity_type(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_entity_type_async( + transport: str = "grpc_asyncio", + request_type=gcdc_entity_type.CreateEntityTypeRequest, +): + client = EntityTypesAsyncClient( + 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_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_entity_type.EntityType( + name="name_value", + display_name="display_name_value", + kind=gcdc_entity_type.EntityType.Kind.KIND_MAP, + auto_expansion_mode=gcdc_entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT, + enable_fuzzy_extraction=True, + ) + ) + + response = await client.create_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_entity_type.CreateEntityTypeRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_entity_type.EntityType) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.kind == gcdc_entity_type.EntityType.Kind.KIND_MAP + + assert ( + response.auto_expansion_mode + == gcdc_entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT + ) + + assert response.enable_fuzzy_extraction is True + + +@pytest.mark.asyncio +async def test_create_entity_type_async_from_dict(): + await test_create_entity_type_async(request_type=dict) + + +def test_create_entity_type_field_headers(): + client = EntityTypesClient(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 = gcdc_entity_type.CreateEntityTypeRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_entity_type), "__call__" + ) as call: + call.return_value = gcdc_entity_type.EntityType() + + client.create_entity_type(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_entity_type_field_headers_async(): + client = EntityTypesAsyncClient(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 = gcdc_entity_type.CreateEntityTypeRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_entity_type), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_entity_type.EntityType() + ) + + await client.create_entity_type(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_entity_type_flattened(): + client = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_entity_type.EntityType() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_entity_type( + parent="parent_value", + entity_type=gcdc_entity_type.EntityType(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].entity_type == gcdc_entity_type.EntityType(name="name_value") + + +def test_create_entity_type_flattened_error(): + client = EntityTypesClient(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_entity_type( + gcdc_entity_type.CreateEntityTypeRequest(), + parent="parent_value", + entity_type=gcdc_entity_type.EntityType(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_entity_type_flattened_async(): + client = EntityTypesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_entity_type.EntityType() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_entity_type.EntityType() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_entity_type( + parent="parent_value", + entity_type=gcdc_entity_type.EntityType(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].entity_type == gcdc_entity_type.EntityType(name="name_value") + + +@pytest.mark.asyncio +async def test_create_entity_type_flattened_error_async(): + client = EntityTypesAsyncClient(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_entity_type( + gcdc_entity_type.CreateEntityTypeRequest(), + parent="parent_value", + entity_type=gcdc_entity_type.EntityType(name="name_value"), + ) + + +def test_update_entity_type( + transport: str = "grpc", request_type=gcdc_entity_type.UpdateEntityTypeRequest +): + client = EntityTypesClient( + 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_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_entity_type.EntityType( + name="name_value", + display_name="display_name_value", + kind=gcdc_entity_type.EntityType.Kind.KIND_MAP, + auto_expansion_mode=gcdc_entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT, + enable_fuzzy_extraction=True, + ) + + response = client.update_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_entity_type.UpdateEntityTypeRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_entity_type.EntityType) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.kind == gcdc_entity_type.EntityType.Kind.KIND_MAP + + assert ( + response.auto_expansion_mode + == gcdc_entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT + ) + + assert response.enable_fuzzy_extraction is True + + +def test_update_entity_type_from_dict(): + test_update_entity_type(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_entity_type_async( + transport: str = "grpc_asyncio", + request_type=gcdc_entity_type.UpdateEntityTypeRequest, +): + client = EntityTypesAsyncClient( + 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_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_entity_type.EntityType( + name="name_value", + display_name="display_name_value", + kind=gcdc_entity_type.EntityType.Kind.KIND_MAP, + auto_expansion_mode=gcdc_entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT, + enable_fuzzy_extraction=True, + ) + ) + + response = await client.update_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_entity_type.UpdateEntityTypeRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_entity_type.EntityType) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.kind == gcdc_entity_type.EntityType.Kind.KIND_MAP + + assert ( + response.auto_expansion_mode + == gcdc_entity_type.EntityType.AutoExpansionMode.AUTO_EXPANSION_MODE_DEFAULT + ) + + assert response.enable_fuzzy_extraction is True + + +@pytest.mark.asyncio +async def test_update_entity_type_async_from_dict(): + await test_update_entity_type_async(request_type=dict) + + +def test_update_entity_type_field_headers(): + client = EntityTypesClient(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 = gcdc_entity_type.UpdateEntityTypeRequest() + request.entity_type.name = "entity_type.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_entity_type), "__call__" + ) as call: + call.return_value = gcdc_entity_type.EntityType() + + client.update_entity_type(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", "entity_type.name=entity_type.name/value",) in kw[ + "metadata" + ] + + +@pytest.mark.asyncio +async def test_update_entity_type_field_headers_async(): + client = EntityTypesAsyncClient(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 = gcdc_entity_type.UpdateEntityTypeRequest() + request.entity_type.name = "entity_type.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_entity_type), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_entity_type.EntityType() + ) + + await client.update_entity_type(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", "entity_type.name=entity_type.name/value",) in kw[ + "metadata" + ] + + +def test_update_entity_type_flattened(): + client = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_entity_type.EntityType() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_entity_type( + entity_type=gcdc_entity_type.EntityType(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].entity_type == gcdc_entity_type.EntityType(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_entity_type_flattened_error(): + client = EntityTypesClient(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_entity_type( + gcdc_entity_type.UpdateEntityTypeRequest(), + entity_type=gcdc_entity_type.EntityType(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_entity_type_flattened_async(): + client = EntityTypesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_entity_type.EntityType() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_entity_type.EntityType() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_entity_type( + entity_type=gcdc_entity_type.EntityType(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].entity_type == gcdc_entity_type.EntityType(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_entity_type_flattened_error_async(): + client = EntityTypesAsyncClient(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_entity_type( + gcdc_entity_type.UpdateEntityTypeRequest(), + entity_type=gcdc_entity_type.EntityType(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_entity_type( + transport: str = "grpc", request_type=entity_type.DeleteEntityTypeRequest +): + client = EntityTypesClient( + 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_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.DeleteEntityTypeRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_entity_type_from_dict(): + test_delete_entity_type(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_entity_type_async( + transport: str = "grpc_asyncio", request_type=entity_type.DeleteEntityTypeRequest +): + client = EntityTypesAsyncClient( + 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_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.DeleteEntityTypeRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_entity_type_async_from_dict(): + await test_delete_entity_type_async(request_type=dict) + + +def test_delete_entity_type_field_headers(): + client = EntityTypesClient(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 = entity_type.DeleteEntityTypeRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_entity_type), "__call__" + ) as call: + call.return_value = None + + client.delete_entity_type(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_entity_type_field_headers_async(): + client = EntityTypesAsyncClient(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 = entity_type.DeleteEntityTypeRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_entity_type), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_entity_type(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_entity_type_flattened(): + client = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_entity_type), "__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_entity_type(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_entity_type_flattened_error(): + client = EntityTypesClient(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_entity_type( + entity_type.DeleteEntityTypeRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_entity_type_flattened_async(): + client = EntityTypesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_entity_type), "__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_entity_type(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_entity_type_flattened_error_async(): + client = EntityTypesAsyncClient(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_entity_type( + entity_type.DeleteEntityTypeRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.EntityTypesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.EntityTypesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = EntityTypesClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.EntityTypesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = EntityTypesClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.EntityTypesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = EntityTypesClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.EntityTypesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.EntityTypesGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], +) +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 = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.EntityTypesGrpcTransport,) + + +def test_entity_types_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.EntityTypesTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_entity_types_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.entity_types.transports.EntityTypesTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.EntityTypesTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_entity_types", + "get_entity_type", + "create_entity_type", + "update_entity_type", + "delete_entity_type", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_entity_types_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.dialogflowcx_v3.services.entity_types.transports.EntityTypesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.EntityTypesTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_entity_types_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.dialogflowcx_v3.services.entity_types.transports.EntityTypesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.EntityTypesTransport() + adc.assert_called_once() + + +def test_entity_types_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) + EntityTypesClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_entity_types_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.EntityTypesGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_entity_types_host_no_port(): + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_entity_types_host_with_port(): + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_entity_types_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.EntityTypesGrpcTransport( + 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_entity_types_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.EntityTypesGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], +) +def test_entity_types_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], +) +def test_entity_types_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_entity_type_path(): + project = "squid" + location = "clam" + agent = "whelk" + entity_type = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + actual = EntityTypesClient.entity_type_path(project, location, agent, entity_type) + assert expected == actual + + +def test_parse_entity_type_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "entity_type": "mussel", + } + path = EntityTypesClient.entity_type_path(**expected) + + # Check that the path construction is reversible. + actual = EntityTypesClient.parse_entity_type_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "winkle" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = EntityTypesClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nautilus", + } + path = EntityTypesClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = EntityTypesClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "scallop" + + expected = "folders/{folder}".format(folder=folder,) + actual = EntityTypesClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "abalone", + } + path = EntityTypesClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = EntityTypesClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "squid" + + expected = "organizations/{organization}".format(organization=organization,) + actual = EntityTypesClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "clam", + } + path = EntityTypesClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = EntityTypesClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "whelk" + + expected = "projects/{project}".format(project=project,) + actual = EntityTypesClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "octopus", + } + path = EntityTypesClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = EntityTypesClient.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 = EntityTypesClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + } + path = EntityTypesClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = EntityTypesClient.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.EntityTypesTransport, "_prep_wrapped_messages" + ) as prep: + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.EntityTypesTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = EntityTypesClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3/test_environments.py b/tests/unit/gapic/dialogflowcx_v3/test_environments.py new file mode 100644 index 00000000..b193e979 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_environments.py @@ -0,0 +1,2495 @@ +# -*- 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 future +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.api_core import operation_async # type: ignore +from google.api_core import operations_v1 +from google.auth import credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.dialogflowcx_v3.services.environments import EnvironmentsAsyncClient +from google.cloud.dialogflowcx_v3.services.environments import EnvironmentsClient +from google.cloud.dialogflowcx_v3.services.environments import pagers +from google.cloud.dialogflowcx_v3.services.environments import transports +from google.cloud.dialogflowcx_v3.types import environment +from google.cloud.dialogflowcx_v3.types import environment as gcdc_environment +from google.longrunning import operations_pb2 +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # 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 EnvironmentsClient._get_default_mtls_endpoint(None) is None + assert ( + EnvironmentsClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + ) + assert ( + EnvironmentsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + EnvironmentsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + EnvironmentsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert EnvironmentsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [EnvironmentsClient, EnvironmentsAsyncClient]) +def test_environments_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 == "dialogflow.googleapis.com:443" + + +def test_environments_client_get_transport_class(): + transport = EnvironmentsClient.get_transport_class() + assert transport == transports.EnvironmentsGrpcTransport + + transport = EnvironmentsClient.get_transport_class("grpc") + assert transport == transports.EnvironmentsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (EnvironmentsClient, transports.EnvironmentsGrpcTransport, "grpc"), + ( + EnvironmentsAsyncClient, + transports.EnvironmentsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + EnvironmentsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(EnvironmentsClient) +) +@mock.patch.object( + EnvironmentsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(EnvironmentsAsyncClient), +) +def test_environments_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(EnvironmentsClient, "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(EnvironmentsClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + (EnvironmentsClient, transports.EnvironmentsGrpcTransport, "grpc", "true"), + ( + EnvironmentsAsyncClient, + transports.EnvironmentsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (EnvironmentsClient, transports.EnvironmentsGrpcTransport, "grpc", "false"), + ( + EnvironmentsAsyncClient, + transports.EnvironmentsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + EnvironmentsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(EnvironmentsClient) +) +@mock.patch.object( + EnvironmentsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(EnvironmentsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_environments_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (EnvironmentsClient, transports.EnvironmentsGrpcTransport, "grpc"), + ( + EnvironmentsAsyncClient, + transports.EnvironmentsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_environments_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (EnvironmentsClient, transports.EnvironmentsGrpcTransport, "grpc"), + ( + EnvironmentsAsyncClient, + transports.EnvironmentsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_environments_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_environments_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.environments.transports.EnvironmentsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = EnvironmentsClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_environments( + transport: str = "grpc", request_type=environment.ListEnvironmentsRequest +): + client = EnvironmentsClient( + 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_environments), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = environment.ListEnvironmentsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_environments(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == environment.ListEnvironmentsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListEnvironmentsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_environments_from_dict(): + test_list_environments(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_environments_async( + transport: str = "grpc_asyncio", request_type=environment.ListEnvironmentsRequest +): + client = EnvironmentsAsyncClient( + 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_environments), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + environment.ListEnvironmentsResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_environments(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == environment.ListEnvironmentsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListEnvironmentsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_environments_async_from_dict(): + await test_list_environments_async(request_type=dict) + + +def test_list_environments_field_headers(): + client = EnvironmentsClient(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 = environment.ListEnvironmentsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_environments), "__call__" + ) as call: + call.return_value = environment.ListEnvironmentsResponse() + + client.list_environments(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_environments_field_headers_async(): + client = EnvironmentsAsyncClient(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 = environment.ListEnvironmentsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_environments), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + environment.ListEnvironmentsResponse() + ) + + await client.list_environments(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_environments_flattened(): + client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_environments), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = environment.ListEnvironmentsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_environments(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_environments_flattened_error(): + client = EnvironmentsClient(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_environments( + environment.ListEnvironmentsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_environments_flattened_async(): + client = EnvironmentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_environments), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = environment.ListEnvironmentsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + environment.ListEnvironmentsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_environments(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_environments_flattened_error_async(): + client = EnvironmentsAsyncClient(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_environments( + environment.ListEnvironmentsRequest(), parent="parent_value", + ) + + +def test_list_environments_pager(): + client = EnvironmentsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_environments), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + environment.ListEnvironmentsResponse( + environments=[ + environment.Environment(), + environment.Environment(), + environment.Environment(), + ], + next_page_token="abc", + ), + environment.ListEnvironmentsResponse( + environments=[], next_page_token="def", + ), + environment.ListEnvironmentsResponse( + environments=[environment.Environment(),], next_page_token="ghi", + ), + environment.ListEnvironmentsResponse( + environments=[environment.Environment(), environment.Environment(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_environments(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, environment.Environment) for i in results) + + +def test_list_environments_pages(): + client = EnvironmentsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_environments), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + environment.ListEnvironmentsResponse( + environments=[ + environment.Environment(), + environment.Environment(), + environment.Environment(), + ], + next_page_token="abc", + ), + environment.ListEnvironmentsResponse( + environments=[], next_page_token="def", + ), + environment.ListEnvironmentsResponse( + environments=[environment.Environment(),], next_page_token="ghi", + ), + environment.ListEnvironmentsResponse( + environments=[environment.Environment(), environment.Environment(),], + ), + RuntimeError, + ) + pages = list(client.list_environments(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_environments_async_pager(): + client = EnvironmentsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_environments), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + environment.ListEnvironmentsResponse( + environments=[ + environment.Environment(), + environment.Environment(), + environment.Environment(), + ], + next_page_token="abc", + ), + environment.ListEnvironmentsResponse( + environments=[], next_page_token="def", + ), + environment.ListEnvironmentsResponse( + environments=[environment.Environment(),], next_page_token="ghi", + ), + environment.ListEnvironmentsResponse( + environments=[environment.Environment(), environment.Environment(),], + ), + RuntimeError, + ) + async_pager = await client.list_environments(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, environment.Environment) for i in responses) + + +@pytest.mark.asyncio +async def test_list_environments_async_pages(): + client = EnvironmentsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_environments), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + environment.ListEnvironmentsResponse( + environments=[ + environment.Environment(), + environment.Environment(), + environment.Environment(), + ], + next_page_token="abc", + ), + environment.ListEnvironmentsResponse( + environments=[], next_page_token="def", + ), + environment.ListEnvironmentsResponse( + environments=[environment.Environment(),], next_page_token="ghi", + ), + environment.ListEnvironmentsResponse( + environments=[environment.Environment(), environment.Environment(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_environments(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_environment( + transport: str = "grpc", request_type=environment.GetEnvironmentRequest +): + client = EnvironmentsClient( + 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_environment), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = environment.Environment( + name="name_value", + display_name="display_name_value", + description="description_value", + ) + + response = client.get_environment(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == environment.GetEnvironmentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, environment.Environment) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + +def test_get_environment_from_dict(): + test_get_environment(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_environment_async( + transport: str = "grpc_asyncio", request_type=environment.GetEnvironmentRequest +): + client = EnvironmentsAsyncClient( + 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_environment), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + environment.Environment( + name="name_value", + display_name="display_name_value", + description="description_value", + ) + ) + + response = await client.get_environment(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == environment.GetEnvironmentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, environment.Environment) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + +@pytest.mark.asyncio +async def test_get_environment_async_from_dict(): + await test_get_environment_async(request_type=dict) + + +def test_get_environment_field_headers(): + client = EnvironmentsClient(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 = environment.GetEnvironmentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_environment), "__call__") as call: + call.return_value = environment.Environment() + + client.get_environment(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_environment_field_headers_async(): + client = EnvironmentsAsyncClient(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 = environment.GetEnvironmentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_environment), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + environment.Environment() + ) + + await client.get_environment(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_environment_flattened(): + client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_environment), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = environment.Environment() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_environment(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_environment_flattened_error(): + client = EnvironmentsClient(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_environment( + environment.GetEnvironmentRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_environment_flattened_async(): + client = EnvironmentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_environment), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = environment.Environment() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + environment.Environment() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_environment(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_environment_flattened_error_async(): + client = EnvironmentsAsyncClient(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_environment( + environment.GetEnvironmentRequest(), name="name_value", + ) + + +def test_create_environment( + transport: str = "grpc", request_type=gcdc_environment.CreateEnvironmentRequest +): + client = EnvironmentsClient( + 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_environment), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.create_environment(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_environment.CreateEnvironmentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_create_environment_from_dict(): + test_create_environment(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_environment_async( + transport: str = "grpc_asyncio", + request_type=gcdc_environment.CreateEnvironmentRequest, +): + client = EnvironmentsAsyncClient( + 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_environment), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + + response = await client.create_environment(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_environment.CreateEnvironmentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_create_environment_async_from_dict(): + await test_create_environment_async(request_type=dict) + + +def test_create_environment_field_headers(): + client = EnvironmentsClient(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 = gcdc_environment.CreateEnvironmentRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_environment), "__call__" + ) as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.create_environment(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_environment_field_headers_async(): + client = EnvironmentsAsyncClient(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 = gcdc_environment.CreateEnvironmentRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_environment), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.create_environment(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_environment_flattened(): + client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_environment), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_environment( + parent="parent_value", + environment=gcdc_environment.Environment(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].environment == gcdc_environment.Environment(name="name_value") + + +def test_create_environment_flattened_error(): + client = EnvironmentsClient(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_environment( + gcdc_environment.CreateEnvironmentRequest(), + parent="parent_value", + environment=gcdc_environment.Environment(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_environment_flattened_async(): + client = EnvironmentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_environment), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_environment( + parent="parent_value", + environment=gcdc_environment.Environment(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].environment == gcdc_environment.Environment(name="name_value") + + +@pytest.mark.asyncio +async def test_create_environment_flattened_error_async(): + client = EnvironmentsAsyncClient(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_environment( + gcdc_environment.CreateEnvironmentRequest(), + parent="parent_value", + environment=gcdc_environment.Environment(name="name_value"), + ) + + +def test_update_environment( + transport: str = "grpc", request_type=gcdc_environment.UpdateEnvironmentRequest +): + client = EnvironmentsClient( + 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_environment), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.update_environment(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_environment.UpdateEnvironmentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_update_environment_from_dict(): + test_update_environment(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_environment_async( + transport: str = "grpc_asyncio", + request_type=gcdc_environment.UpdateEnvironmentRequest, +): + client = EnvironmentsAsyncClient( + 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_environment), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + + response = await client.update_environment(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_environment.UpdateEnvironmentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_update_environment_async_from_dict(): + await test_update_environment_async(request_type=dict) + + +def test_update_environment_field_headers(): + client = EnvironmentsClient(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 = gcdc_environment.UpdateEnvironmentRequest() + request.environment.name = "environment.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_environment), "__call__" + ) as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.update_environment(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", "environment.name=environment.name/value",) in kw[ + "metadata" + ] + + +@pytest.mark.asyncio +async def test_update_environment_field_headers_async(): + client = EnvironmentsAsyncClient(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 = gcdc_environment.UpdateEnvironmentRequest() + request.environment.name = "environment.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_environment), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.update_environment(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", "environment.name=environment.name/value",) in kw[ + "metadata" + ] + + +def test_update_environment_flattened(): + client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_environment), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_environment( + environment=gcdc_environment.Environment(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].environment == gcdc_environment.Environment(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_environment_flattened_error(): + client = EnvironmentsClient(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_environment( + gcdc_environment.UpdateEnvironmentRequest(), + environment=gcdc_environment.Environment(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_environment_flattened_async(): + client = EnvironmentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_environment), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_environment( + environment=gcdc_environment.Environment(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].environment == gcdc_environment.Environment(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_environment_flattened_error_async(): + client = EnvironmentsAsyncClient(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_environment( + gcdc_environment.UpdateEnvironmentRequest(), + environment=gcdc_environment.Environment(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_environment( + transport: str = "grpc", request_type=environment.DeleteEnvironmentRequest +): + client = EnvironmentsClient( + 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_environment), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_environment(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == environment.DeleteEnvironmentRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_environment_from_dict(): + test_delete_environment(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_environment_async( + transport: str = "grpc_asyncio", request_type=environment.DeleteEnvironmentRequest +): + client = EnvironmentsAsyncClient( + 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_environment), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_environment(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == environment.DeleteEnvironmentRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_environment_async_from_dict(): + await test_delete_environment_async(request_type=dict) + + +def test_delete_environment_field_headers(): + client = EnvironmentsClient(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 = environment.DeleteEnvironmentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_environment), "__call__" + ) as call: + call.return_value = None + + client.delete_environment(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_environment_field_headers_async(): + client = EnvironmentsAsyncClient(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 = environment.DeleteEnvironmentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_environment), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_environment(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_environment_flattened(): + client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_environment), "__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_environment(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_environment_flattened_error(): + client = EnvironmentsClient(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_environment( + environment.DeleteEnvironmentRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_environment_flattened_async(): + client = EnvironmentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_environment), "__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_environment(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_environment_flattened_error_async(): + client = EnvironmentsAsyncClient(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_environment( + environment.DeleteEnvironmentRequest(), name="name_value", + ) + + +def test_lookup_environment_history( + transport: str = "grpc", request_type=environment.LookupEnvironmentHistoryRequest +): + client = EnvironmentsClient( + 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.lookup_environment_history), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = environment.LookupEnvironmentHistoryResponse( + next_page_token="next_page_token_value", + ) + + response = client.lookup_environment_history(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == environment.LookupEnvironmentHistoryRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.LookupEnvironmentHistoryPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_lookup_environment_history_from_dict(): + test_lookup_environment_history(request_type=dict) + + +@pytest.mark.asyncio +async def test_lookup_environment_history_async( + transport: str = "grpc_asyncio", + request_type=environment.LookupEnvironmentHistoryRequest, +): + client = EnvironmentsAsyncClient( + 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.lookup_environment_history), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + environment.LookupEnvironmentHistoryResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.lookup_environment_history(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == environment.LookupEnvironmentHistoryRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.LookupEnvironmentHistoryAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_lookup_environment_history_async_from_dict(): + await test_lookup_environment_history_async(request_type=dict) + + +def test_lookup_environment_history_field_headers(): + client = EnvironmentsClient(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 = environment.LookupEnvironmentHistoryRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.lookup_environment_history), "__call__" + ) as call: + call.return_value = environment.LookupEnvironmentHistoryResponse() + + client.lookup_environment_history(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_lookup_environment_history_field_headers_async(): + client = EnvironmentsAsyncClient(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 = environment.LookupEnvironmentHistoryRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.lookup_environment_history), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + environment.LookupEnvironmentHistoryResponse() + ) + + await client.lookup_environment_history(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_lookup_environment_history_flattened(): + client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.lookup_environment_history), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = environment.LookupEnvironmentHistoryResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.lookup_environment_history(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_lookup_environment_history_flattened_error(): + client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.lookup_environment_history( + environment.LookupEnvironmentHistoryRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_lookup_environment_history_flattened_async(): + client = EnvironmentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.lookup_environment_history), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = environment.LookupEnvironmentHistoryResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + environment.LookupEnvironmentHistoryResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.lookup_environment_history(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_lookup_environment_history_flattened_error_async(): + client = EnvironmentsAsyncClient(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.lookup_environment_history( + environment.LookupEnvironmentHistoryRequest(), name="name_value", + ) + + +def test_lookup_environment_history_pager(): + client = EnvironmentsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.lookup_environment_history), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + environment.LookupEnvironmentHistoryResponse( + environments=[ + environment.Environment(), + environment.Environment(), + environment.Environment(), + ], + next_page_token="abc", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[], next_page_token="def", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[environment.Environment(),], next_page_token="ghi", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[environment.Environment(), environment.Environment(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", ""),)), + ) + pager = client.lookup_environment_history(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, environment.Environment) for i in results) + + +def test_lookup_environment_history_pages(): + client = EnvironmentsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.lookup_environment_history), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + environment.LookupEnvironmentHistoryResponse( + environments=[ + environment.Environment(), + environment.Environment(), + environment.Environment(), + ], + next_page_token="abc", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[], next_page_token="def", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[environment.Environment(),], next_page_token="ghi", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[environment.Environment(), environment.Environment(),], + ), + RuntimeError, + ) + pages = list(client.lookup_environment_history(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_lookup_environment_history_async_pager(): + client = EnvironmentsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.lookup_environment_history), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + environment.LookupEnvironmentHistoryResponse( + environments=[ + environment.Environment(), + environment.Environment(), + environment.Environment(), + ], + next_page_token="abc", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[], next_page_token="def", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[environment.Environment(),], next_page_token="ghi", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[environment.Environment(), environment.Environment(),], + ), + RuntimeError, + ) + async_pager = await client.lookup_environment_history(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, environment.Environment) for i in responses) + + +@pytest.mark.asyncio +async def test_lookup_environment_history_async_pages(): + client = EnvironmentsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.lookup_environment_history), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + environment.LookupEnvironmentHistoryResponse( + environments=[ + environment.Environment(), + environment.Environment(), + environment.Environment(), + ], + next_page_token="abc", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[], next_page_token="def", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[environment.Environment(),], next_page_token="ghi", + ), + environment.LookupEnvironmentHistoryResponse( + environments=[environment.Environment(), environment.Environment(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.lookup_environment_history(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.EnvironmentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = EnvironmentsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.EnvironmentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = EnvironmentsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.EnvironmentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = EnvironmentsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.EnvironmentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = EnvironmentsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.EnvironmentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.EnvironmentsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], +) +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 = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.EnvironmentsGrpcTransport,) + + +def test_environments_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.EnvironmentsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_environments_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.environments.transports.EnvironmentsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.EnvironmentsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_environments", + "get_environment", + "create_environment", + "update_environment", + "delete_environment", + "lookup_environment_history", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + # Additionally, the LRO client (a property) should + # also raise NotImplementedError + with pytest.raises(NotImplementedError): + transport.operations_client + + +def test_environments_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.dialogflowcx_v3.services.environments.transports.EnvironmentsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.EnvironmentsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_environments_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.dialogflowcx_v3.services.environments.transports.EnvironmentsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.EnvironmentsTransport() + adc.assert_called_once() + + +def test_environments_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) + EnvironmentsClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_environments_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.EnvironmentsGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_environments_host_no_port(): + client = EnvironmentsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_environments_host_with_port(): + client = EnvironmentsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_environments_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.EnvironmentsGrpcTransport( + 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_environments_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.EnvironmentsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], +) +def test_environments_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], +) +def test_environments_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_environments_grpc_lro_client(): + client = EnvironmentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_environments_grpc_lro_async_client(): + client = EnvironmentsAsyncClient( + credentials=credentials.AnonymousCredentials(), transport="grpc_asyncio", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_environment_path(): + project = "squid" + location = "clam" + agent = "whelk" + environment = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/environments/{environment}".format( + project=project, location=location, agent=agent, environment=environment, + ) + actual = EnvironmentsClient.environment_path(project, location, agent, environment) + assert expected == actual + + +def test_parse_environment_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "environment": "mussel", + } + path = EnvironmentsClient.environment_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_environment_path(path) + assert expected == actual + + +def test_version_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + flow = "abalone" + version = "squid" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/versions/{version}".format( + project=project, location=location, agent=agent, flow=flow, version=version, + ) + actual = EnvironmentsClient.version_path(project, location, agent, flow, version) + assert expected == actual + + +def test_parse_version_path(): + expected = { + "project": "clam", + "location": "whelk", + "agent": "octopus", + "flow": "oyster", + "version": "nudibranch", + } + path = EnvironmentsClient.version_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_version_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "cuttlefish" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = EnvironmentsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "mussel", + } + path = EnvironmentsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "winkle" + + expected = "folders/{folder}".format(folder=folder,) + actual = EnvironmentsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nautilus", + } + path = EnvironmentsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "scallop" + + expected = "organizations/{organization}".format(organization=organization,) + actual = EnvironmentsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "abalone", + } + path = EnvironmentsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "squid" + + expected = "projects/{project}".format(project=project,) + actual = EnvironmentsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "clam", + } + path = EnvironmentsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "whelk" + location = "octopus" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = EnvironmentsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + } + path = EnvironmentsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.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.EnvironmentsTransport, "_prep_wrapped_messages" + ) as prep: + client = EnvironmentsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.EnvironmentsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = EnvironmentsClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3/test_flows.py b/tests/unit/gapic/dialogflowcx_v3/test_flows.py new file mode 100644 index 00000000..3de55597 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_flows.py @@ -0,0 +1,2256 @@ +# -*- 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 future +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.api_core import operation_async # type: ignore +from google.api_core import operations_v1 +from google.auth import credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.dialogflowcx_v3.services.flows import FlowsAsyncClient +from google.cloud.dialogflowcx_v3.services.flows import FlowsClient +from google.cloud.dialogflowcx_v3.services.flows import pagers +from google.cloud.dialogflowcx_v3.services.flows import transports +from google.cloud.dialogflowcx_v3.types import flow +from google.cloud.dialogflowcx_v3.types import flow as gcdc_flow +from google.cloud.dialogflowcx_v3.types import fulfillment +from google.cloud.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import response_message +from google.longrunning import operations_pb2 +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # 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 FlowsClient._get_default_mtls_endpoint(None) is None + assert FlowsClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + FlowsClient._get_default_mtls_endpoint(api_mtls_endpoint) == api_mtls_endpoint + ) + assert ( + FlowsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + FlowsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert FlowsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [FlowsClient, FlowsAsyncClient]) +def test_flows_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 == "dialogflow.googleapis.com:443" + + +def test_flows_client_get_transport_class(): + transport = FlowsClient.get_transport_class() + assert transport == transports.FlowsGrpcTransport + + transport = FlowsClient.get_transport_class("grpc") + assert transport == transports.FlowsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (FlowsClient, transports.FlowsGrpcTransport, "grpc"), + (FlowsAsyncClient, transports.FlowsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +@mock.patch.object( + FlowsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(FlowsClient) +) +@mock.patch.object( + FlowsAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(FlowsAsyncClient) +) +def test_flows_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(FlowsClient, "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(FlowsClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + (FlowsClient, transports.FlowsGrpcTransport, "grpc", "true"), + ( + FlowsAsyncClient, + transports.FlowsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (FlowsClient, transports.FlowsGrpcTransport, "grpc", "false"), + ( + FlowsAsyncClient, + transports.FlowsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + FlowsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(FlowsClient) +) +@mock.patch.object( + FlowsAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(FlowsAsyncClient) +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_flows_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (FlowsClient, transports.FlowsGrpcTransport, "grpc"), + (FlowsAsyncClient, transports.FlowsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_flows_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (FlowsClient, transports.FlowsGrpcTransport, "grpc"), + (FlowsAsyncClient, transports.FlowsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_flows_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_flows_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.flows.transports.FlowsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = FlowsClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_create_flow(transport: str = "grpc", request_type=gcdc_flow.CreateFlowRequest): + client = FlowsClient( + 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_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_flow.Flow( + name="name_value", + display_name="display_name_value", + description="description_value", + ) + + response = client.create_flow(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_flow.CreateFlowRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_flow.Flow) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + +def test_create_flow_from_dict(): + test_create_flow(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_flow_async( + transport: str = "grpc_asyncio", request_type=gcdc_flow.CreateFlowRequest +): + client = FlowsAsyncClient( + 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_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_flow.Flow( + name="name_value", + display_name="display_name_value", + description="description_value", + ) + ) + + response = await client.create_flow(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_flow.CreateFlowRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_flow.Flow) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + +@pytest.mark.asyncio +async def test_create_flow_async_from_dict(): + await test_create_flow_async(request_type=dict) + + +def test_create_flow_field_headers(): + client = FlowsClient(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 = gcdc_flow.CreateFlowRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_flow), "__call__") as call: + call.return_value = gcdc_flow.Flow() + + client.create_flow(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_flow_field_headers_async(): + client = FlowsAsyncClient(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 = gcdc_flow.CreateFlowRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_flow), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_flow.Flow()) + + await client.create_flow(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_flow_flattened(): + client = FlowsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_flow.Flow() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_flow( + parent="parent_value", flow=gcdc_flow.Flow(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].flow == gcdc_flow.Flow(name="name_value") + + +def test_create_flow_flattened_error(): + client = FlowsClient(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_flow( + gcdc_flow.CreateFlowRequest(), + parent="parent_value", + flow=gcdc_flow.Flow(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_flow_flattened_async(): + client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_flow.Flow() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_flow.Flow()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_flow( + parent="parent_value", flow=gcdc_flow.Flow(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].flow == gcdc_flow.Flow(name="name_value") + + +@pytest.mark.asyncio +async def test_create_flow_flattened_error_async(): + client = FlowsAsyncClient(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_flow( + gcdc_flow.CreateFlowRequest(), + parent="parent_value", + flow=gcdc_flow.Flow(name="name_value"), + ) + + +def test_delete_flow(transport: str = "grpc", request_type=flow.DeleteFlowRequest): + client = FlowsClient( + 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_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_flow(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == flow.DeleteFlowRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_flow_from_dict(): + test_delete_flow(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_flow_async( + transport: str = "grpc_asyncio", request_type=flow.DeleteFlowRequest +): + client = FlowsAsyncClient( + 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_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_flow(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == flow.DeleteFlowRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_flow_async_from_dict(): + await test_delete_flow_async(request_type=dict) + + +def test_delete_flow_field_headers(): + client = FlowsClient(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 = flow.DeleteFlowRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_flow), "__call__") as call: + call.return_value = None + + client.delete_flow(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_flow_field_headers_async(): + client = FlowsAsyncClient(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 = flow.DeleteFlowRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_flow), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_flow(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_flow_flattened(): + client = FlowsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_flow), "__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_flow(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_flow_flattened_error(): + client = FlowsClient(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_flow( + flow.DeleteFlowRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_flow_flattened_async(): + client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_flow), "__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_flow(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_flow_flattened_error_async(): + client = FlowsAsyncClient(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_flow( + flow.DeleteFlowRequest(), name="name_value", + ) + + +def test_list_flows(transport: str = "grpc", request_type=flow.ListFlowsRequest): + client = FlowsClient( + 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_flows), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = flow.ListFlowsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_flows(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == flow.ListFlowsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListFlowsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_flows_from_dict(): + test_list_flows(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_flows_async( + transport: str = "grpc_asyncio", request_type=flow.ListFlowsRequest +): + client = FlowsAsyncClient( + 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_flows), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + flow.ListFlowsResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_flows(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == flow.ListFlowsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListFlowsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_flows_async_from_dict(): + await test_list_flows_async(request_type=dict) + + +def test_list_flows_field_headers(): + client = FlowsClient(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 = flow.ListFlowsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: + call.return_value = flow.ListFlowsResponse() + + client.list_flows(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_flows_field_headers_async(): + client = FlowsAsyncClient(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 = flow.ListFlowsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + flow.ListFlowsResponse() + ) + + await client.list_flows(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_flows_flattened(): + client = FlowsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = flow.ListFlowsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_flows(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_flows_flattened_error(): + client = FlowsClient(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_flows( + flow.ListFlowsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_flows_flattened_async(): + client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = flow.ListFlowsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + flow.ListFlowsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_flows(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_flows_flattened_error_async(): + client = FlowsAsyncClient(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_flows( + flow.ListFlowsRequest(), parent="parent_value", + ) + + +def test_list_flows_pager(): + client = FlowsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + flow.ListFlowsResponse( + flows=[flow.Flow(), flow.Flow(), flow.Flow(),], next_page_token="abc", + ), + flow.ListFlowsResponse(flows=[], next_page_token="def",), + flow.ListFlowsResponse(flows=[flow.Flow(),], next_page_token="ghi",), + flow.ListFlowsResponse(flows=[flow.Flow(), flow.Flow(),],), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_flows(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, flow.Flow) for i in results) + + +def test_list_flows_pages(): + client = FlowsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + flow.ListFlowsResponse( + flows=[flow.Flow(), flow.Flow(), flow.Flow(),], next_page_token="abc", + ), + flow.ListFlowsResponse(flows=[], next_page_token="def",), + flow.ListFlowsResponse(flows=[flow.Flow(),], next_page_token="ghi",), + flow.ListFlowsResponse(flows=[flow.Flow(), flow.Flow(),],), + RuntimeError, + ) + pages = list(client.list_flows(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_flows_async_pager(): + client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_flows), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + flow.ListFlowsResponse( + flows=[flow.Flow(), flow.Flow(), flow.Flow(),], next_page_token="abc", + ), + flow.ListFlowsResponse(flows=[], next_page_token="def",), + flow.ListFlowsResponse(flows=[flow.Flow(),], next_page_token="ghi",), + flow.ListFlowsResponse(flows=[flow.Flow(), flow.Flow(),],), + RuntimeError, + ) + async_pager = await client.list_flows(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, flow.Flow) for i in responses) + + +@pytest.mark.asyncio +async def test_list_flows_async_pages(): + client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_flows), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + flow.ListFlowsResponse( + flows=[flow.Flow(), flow.Flow(), flow.Flow(),], next_page_token="abc", + ), + flow.ListFlowsResponse(flows=[], next_page_token="def",), + flow.ListFlowsResponse(flows=[flow.Flow(),], next_page_token="ghi",), + flow.ListFlowsResponse(flows=[flow.Flow(), flow.Flow(),],), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_flows(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_flow(transport: str = "grpc", request_type=flow.GetFlowRequest): + client = FlowsClient( + 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_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = flow.Flow( + name="name_value", + display_name="display_name_value", + description="description_value", + ) + + response = client.get_flow(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == flow.GetFlowRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, flow.Flow) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + +def test_get_flow_from_dict(): + test_get_flow(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_flow_async( + transport: str = "grpc_asyncio", request_type=flow.GetFlowRequest +): + client = FlowsAsyncClient( + 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_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + flow.Flow( + name="name_value", + display_name="display_name_value", + description="description_value", + ) + ) + + response = await client.get_flow(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == flow.GetFlowRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, flow.Flow) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + +@pytest.mark.asyncio +async def test_get_flow_async_from_dict(): + await test_get_flow_async(request_type=dict) + + +def test_get_flow_field_headers(): + client = FlowsClient(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 = flow.GetFlowRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_flow), "__call__") as call: + call.return_value = flow.Flow() + + client.get_flow(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_flow_field_headers_async(): + client = FlowsAsyncClient(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 = flow.GetFlowRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_flow), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(flow.Flow()) + + await client.get_flow(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_flow_flattened(): + client = FlowsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = flow.Flow() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_flow(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_flow_flattened_error(): + client = FlowsClient(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_flow( + flow.GetFlowRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_flow_flattened_async(): + client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = flow.Flow() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(flow.Flow()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_flow(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_flow_flattened_error_async(): + client = FlowsAsyncClient(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_flow( + flow.GetFlowRequest(), name="name_value", + ) + + +def test_update_flow(transport: str = "grpc", request_type=gcdc_flow.UpdateFlowRequest): + client = FlowsClient( + 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_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_flow.Flow( + name="name_value", + display_name="display_name_value", + description="description_value", + ) + + response = client.update_flow(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_flow.UpdateFlowRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_flow.Flow) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + +def test_update_flow_from_dict(): + test_update_flow(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_flow_async( + transport: str = "grpc_asyncio", request_type=gcdc_flow.UpdateFlowRequest +): + client = FlowsAsyncClient( + 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_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_flow.Flow( + name="name_value", + display_name="display_name_value", + description="description_value", + ) + ) + + response = await client.update_flow(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_flow.UpdateFlowRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_flow.Flow) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + +@pytest.mark.asyncio +async def test_update_flow_async_from_dict(): + await test_update_flow_async(request_type=dict) + + +def test_update_flow_field_headers(): + client = FlowsClient(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 = gcdc_flow.UpdateFlowRequest() + request.flow.name = "flow.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_flow), "__call__") as call: + call.return_value = gcdc_flow.Flow() + + client.update_flow(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", "flow.name=flow.name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_flow_field_headers_async(): + client = FlowsAsyncClient(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 = gcdc_flow.UpdateFlowRequest() + request.flow.name = "flow.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_flow), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_flow.Flow()) + + await client.update_flow(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", "flow.name=flow.name/value",) in kw["metadata"] + + +def test_update_flow_flattened(): + client = FlowsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_flow.Flow() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_flow( + flow=gcdc_flow.Flow(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].flow == gcdc_flow.Flow(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_flow_flattened_error(): + client = FlowsClient(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_flow( + gcdc_flow.UpdateFlowRequest(), + flow=gcdc_flow.Flow(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_flow_flattened_async(): + client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_flow.Flow() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_flow.Flow()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_flow( + flow=gcdc_flow.Flow(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].flow == gcdc_flow.Flow(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_flow_flattened_error_async(): + client = FlowsAsyncClient(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_flow( + gcdc_flow.UpdateFlowRequest(), + flow=gcdc_flow.Flow(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_train_flow(transport: str = "grpc", request_type=flow.TrainFlowRequest): + client = FlowsClient( + 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.train_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.train_flow(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == flow.TrainFlowRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_train_flow_from_dict(): + test_train_flow(request_type=dict) + + +@pytest.mark.asyncio +async def test_train_flow_async( + transport: str = "grpc_asyncio", request_type=flow.TrainFlowRequest +): + client = FlowsAsyncClient( + 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.train_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + + response = await client.train_flow(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == flow.TrainFlowRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_train_flow_async_from_dict(): + await test_train_flow_async(request_type=dict) + + +def test_train_flow_field_headers(): + client = FlowsClient(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 = flow.TrainFlowRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.train_flow), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.train_flow(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_train_flow_field_headers_async(): + client = FlowsAsyncClient(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 = flow.TrainFlowRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.train_flow), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.train_flow(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_train_flow_flattened(): + client = FlowsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.train_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.train_flow(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_train_flow_flattened_error(): + client = FlowsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.train_flow( + flow.TrainFlowRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_train_flow_flattened_async(): + client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.train_flow), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.train_flow(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_train_flow_flattened_error_async(): + client = FlowsAsyncClient(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.train_flow( + flow.TrainFlowRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.FlowsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = FlowsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.FlowsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = FlowsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.FlowsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = FlowsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.FlowsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = FlowsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.FlowsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.FlowsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.FlowsGrpcTransport, transports.FlowsGrpcAsyncIOTransport], +) +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 = FlowsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.FlowsGrpcTransport,) + + +def test_flows_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.FlowsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_flows_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.flows.transports.FlowsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.FlowsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "create_flow", + "delete_flow", + "list_flows", + "get_flow", + "update_flow", + "train_flow", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + # Additionally, the LRO client (a property) should + # also raise NotImplementedError + with pytest.raises(NotImplementedError): + transport.operations_client + + +def test_flows_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.dialogflowcx_v3.services.flows.transports.FlowsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.FlowsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_flows_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.dialogflowcx_v3.services.flows.transports.FlowsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.FlowsTransport() + adc.assert_called_once() + + +def test_flows_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) + FlowsClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_flows_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.FlowsGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_flows_host_no_port(): + client = FlowsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_flows_host_with_port(): + client = FlowsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_flows_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.FlowsGrpcTransport(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_flows_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.FlowsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [transports.FlowsGrpcTransport, transports.FlowsGrpcAsyncIOTransport], +) +def test_flows_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [transports.FlowsGrpcTransport, transports.FlowsGrpcAsyncIOTransport], +) +def test_flows_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_flows_grpc_lro_client(): + client = FlowsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_flows_grpc_lro_async_client(): + client = FlowsAsyncClient( + credentials=credentials.AnonymousCredentials(), transport="grpc_asyncio", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_flow_path(): + project = "squid" + location = "clam" + agent = "whelk" + flow = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + actual = FlowsClient.flow_path(project, location, agent, flow) + assert expected == actual + + +def test_parse_flow_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "flow": "mussel", + } + path = FlowsClient.flow_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_flow_path(path) + assert expected == actual + + +def test_intent_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + intent = "abalone" + + expected = "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + actual = FlowsClient.intent_path(project, location, agent, intent) + assert expected == actual + + +def test_parse_intent_path(): + expected = { + "project": "squid", + "location": "clam", + "agent": "whelk", + "intent": "octopus", + } + path = FlowsClient.intent_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_intent_path(path) + assert expected == actual + + +def test_page_path(): + project = "oyster" + location = "nudibranch" + agent = "cuttlefish" + flow = "mussel" + page = "winkle" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + actual = FlowsClient.page_path(project, location, agent, flow, page) + assert expected == actual + + +def test_parse_page_path(): + expected = { + "project": "nautilus", + "location": "scallop", + "agent": "abalone", + "flow": "squid", + "page": "clam", + } + path = FlowsClient.page_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_page_path(path) + assert expected == actual + + +def test_webhook_path(): + project = "whelk" + location = "octopus" + agent = "oyster" + webhook = "nudibranch" + + expected = "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + actual = FlowsClient.webhook_path(project, location, agent, webhook) + assert expected == actual + + +def test_parse_webhook_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + "agent": "winkle", + "webhook": "nautilus", + } + path = FlowsClient.webhook_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_webhook_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "scallop" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = FlowsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "abalone", + } + path = FlowsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "squid" + + expected = "folders/{folder}".format(folder=folder,) + actual = FlowsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "clam", + } + path = FlowsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "whelk" + + expected = "organizations/{organization}".format(organization=organization,) + actual = FlowsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "octopus", + } + path = FlowsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "oyster" + + expected = "projects/{project}".format(project=project,) + actual = FlowsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nudibranch", + } + path = FlowsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "cuttlefish" + location = "mussel" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = FlowsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "winkle", + "location": "nautilus", + } + path = FlowsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.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.FlowsTransport, "_prep_wrapped_messages") as prep: + client = FlowsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object(transports.FlowsTransport, "_prep_wrapped_messages") as prep: + transport_class = FlowsClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3/test_intents.py b/tests/unit/gapic/dialogflowcx_v3/test_intents.py new file mode 100644 index 00000000..f92d9461 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_intents.py @@ -0,0 +1,2036 @@ +# -*- 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.dialogflowcx_v3.services.intents import IntentsAsyncClient +from google.cloud.dialogflowcx_v3.services.intents import IntentsClient +from google.cloud.dialogflowcx_v3.services.intents import pagers +from google.cloud.dialogflowcx_v3.services.intents import transports +from google.cloud.dialogflowcx_v3.types import intent +from google.cloud.dialogflowcx_v3.types import intent as gcdc_intent +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # 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 IntentsClient._get_default_mtls_endpoint(None) is None + assert IntentsClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + IntentsClient._get_default_mtls_endpoint(api_mtls_endpoint) == api_mtls_endpoint + ) + assert ( + IntentsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + IntentsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert IntentsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [IntentsClient, IntentsAsyncClient]) +def test_intents_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 == "dialogflow.googleapis.com:443" + + +def test_intents_client_get_transport_class(): + transport = IntentsClient.get_transport_class() + assert transport == transports.IntentsGrpcTransport + + transport = IntentsClient.get_transport_class("grpc") + assert transport == transports.IntentsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (IntentsClient, transports.IntentsGrpcTransport, "grpc"), + (IntentsAsyncClient, transports.IntentsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +@mock.patch.object( + IntentsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(IntentsClient) +) +@mock.patch.object( + IntentsAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(IntentsAsyncClient) +) +def test_intents_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(IntentsClient, "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(IntentsClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + (IntentsClient, transports.IntentsGrpcTransport, "grpc", "true"), + ( + IntentsAsyncClient, + transports.IntentsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (IntentsClient, transports.IntentsGrpcTransport, "grpc", "false"), + ( + IntentsAsyncClient, + transports.IntentsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + IntentsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(IntentsClient) +) +@mock.patch.object( + IntentsAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(IntentsAsyncClient) +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_intents_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (IntentsClient, transports.IntentsGrpcTransport, "grpc"), + (IntentsAsyncClient, transports.IntentsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_intents_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (IntentsClient, transports.IntentsGrpcTransport, "grpc"), + (IntentsAsyncClient, transports.IntentsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_intents_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_intents_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.intents.transports.IntentsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = IntentsClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_intents(transport: str = "grpc", request_type=intent.ListIntentsRequest): + client = IntentsClient( + 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_intents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = intent.ListIntentsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_intents(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.ListIntentsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListIntentsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_intents_from_dict(): + test_list_intents(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_intents_async( + transport: str = "grpc_asyncio", request_type=intent.ListIntentsRequest +): + client = IntentsAsyncClient( + 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_intents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + intent.ListIntentsResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_intents(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.ListIntentsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListIntentsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_intents_async_from_dict(): + await test_list_intents_async(request_type=dict) + + +def test_list_intents_field_headers(): + client = IntentsClient(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 = intent.ListIntentsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: + call.return_value = intent.ListIntentsResponse() + + client.list_intents(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_intents_field_headers_async(): + client = IntentsAsyncClient(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 = intent.ListIntentsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + intent.ListIntentsResponse() + ) + + await client.list_intents(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_intents_flattened(): + client = IntentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = intent.ListIntentsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_intents(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_intents_flattened_error(): + client = IntentsClient(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_intents( + intent.ListIntentsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_intents_flattened_async(): + client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = intent.ListIntentsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + intent.ListIntentsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_intents(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_intents_flattened_error_async(): + client = IntentsAsyncClient(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_intents( + intent.ListIntentsRequest(), parent="parent_value", + ) + + +def test_list_intents_pager(): + client = IntentsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + intent.ListIntentsResponse( + intents=[intent.Intent(), intent.Intent(), intent.Intent(),], + next_page_token="abc", + ), + intent.ListIntentsResponse(intents=[], next_page_token="def",), + intent.ListIntentsResponse( + intents=[intent.Intent(),], next_page_token="ghi", + ), + intent.ListIntentsResponse(intents=[intent.Intent(), intent.Intent(),],), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_intents(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, intent.Intent) for i in results) + + +def test_list_intents_pages(): + client = IntentsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + intent.ListIntentsResponse( + intents=[intent.Intent(), intent.Intent(), intent.Intent(),], + next_page_token="abc", + ), + intent.ListIntentsResponse(intents=[], next_page_token="def",), + intent.ListIntentsResponse( + intents=[intent.Intent(),], next_page_token="ghi", + ), + intent.ListIntentsResponse(intents=[intent.Intent(), intent.Intent(),],), + RuntimeError, + ) + pages = list(client.list_intents(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_intents_async_pager(): + client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_intents), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + intent.ListIntentsResponse( + intents=[intent.Intent(), intent.Intent(), intent.Intent(),], + next_page_token="abc", + ), + intent.ListIntentsResponse(intents=[], next_page_token="def",), + intent.ListIntentsResponse( + intents=[intent.Intent(),], next_page_token="ghi", + ), + intent.ListIntentsResponse(intents=[intent.Intent(), intent.Intent(),],), + RuntimeError, + ) + async_pager = await client.list_intents(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, intent.Intent) for i in responses) + + +@pytest.mark.asyncio +async def test_list_intents_async_pages(): + client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_intents), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + intent.ListIntentsResponse( + intents=[intent.Intent(), intent.Intent(), intent.Intent(),], + next_page_token="abc", + ), + intent.ListIntentsResponse(intents=[], next_page_token="def",), + intent.ListIntentsResponse( + intents=[intent.Intent(),], next_page_token="ghi", + ), + intent.ListIntentsResponse(intents=[intent.Intent(), intent.Intent(),],), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_intents(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_intent(transport: str = "grpc", request_type=intent.GetIntentRequest): + client = IntentsClient( + 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_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = intent.Intent( + name="name_value", + display_name="display_name_value", + priority=898, + is_fallback=True, + description="description_value", + ) + + response = client.get_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.GetIntentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, intent.Intent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.priority == 898 + + assert response.is_fallback is True + + assert response.description == "description_value" + + +def test_get_intent_from_dict(): + test_get_intent(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_intent_async( + transport: str = "grpc_asyncio", request_type=intent.GetIntentRequest +): + client = IntentsAsyncClient( + 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_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + intent.Intent( + name="name_value", + display_name="display_name_value", + priority=898, + is_fallback=True, + description="description_value", + ) + ) + + response = await client.get_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.GetIntentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, intent.Intent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.priority == 898 + + assert response.is_fallback is True + + assert response.description == "description_value" + + +@pytest.mark.asyncio +async def test_get_intent_async_from_dict(): + await test_get_intent_async(request_type=dict) + + +def test_get_intent_field_headers(): + client = IntentsClient(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 = intent.GetIntentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: + call.return_value = intent.Intent() + + client.get_intent(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_intent_field_headers_async(): + client = IntentsAsyncClient(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 = intent.GetIntentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(intent.Intent()) + + await client.get_intent(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_intent_flattened(): + client = IntentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = intent.Intent() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_intent(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_intent_flattened_error(): + client = IntentsClient(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_intent( + intent.GetIntentRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_intent_flattened_async(): + client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = intent.Intent() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(intent.Intent()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_intent(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_intent_flattened_error_async(): + client = IntentsAsyncClient(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_intent( + intent.GetIntentRequest(), name="name_value", + ) + + +def test_create_intent( + transport: str = "grpc", request_type=gcdc_intent.CreateIntentRequest +): + client = IntentsClient( + 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_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_intent.Intent( + name="name_value", + display_name="display_name_value", + priority=898, + is_fallback=True, + description="description_value", + ) + + response = client.create_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_intent.CreateIntentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_intent.Intent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.priority == 898 + + assert response.is_fallback is True + + assert response.description == "description_value" + + +def test_create_intent_from_dict(): + test_create_intent(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_intent_async( + transport: str = "grpc_asyncio", request_type=gcdc_intent.CreateIntentRequest +): + client = IntentsAsyncClient( + 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_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_intent.Intent( + name="name_value", + display_name="display_name_value", + priority=898, + is_fallback=True, + description="description_value", + ) + ) + + response = await client.create_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_intent.CreateIntentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_intent.Intent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.priority == 898 + + assert response.is_fallback is True + + assert response.description == "description_value" + + +@pytest.mark.asyncio +async def test_create_intent_async_from_dict(): + await test_create_intent_async(request_type=dict) + + +def test_create_intent_field_headers(): + client = IntentsClient(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 = gcdc_intent.CreateIntentRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: + call.return_value = gcdc_intent.Intent() + + client.create_intent(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_intent_field_headers_async(): + client = IntentsAsyncClient(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 = gcdc_intent.CreateIntentRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_intent.Intent()) + + await client.create_intent(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_intent_flattened(): + client = IntentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_intent.Intent() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_intent( + parent="parent_value", intent=gcdc_intent.Intent(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].intent == gcdc_intent.Intent(name="name_value") + + +def test_create_intent_flattened_error(): + client = IntentsClient(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_intent( + gcdc_intent.CreateIntentRequest(), + parent="parent_value", + intent=gcdc_intent.Intent(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_intent_flattened_async(): + client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_intent.Intent() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_intent.Intent()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_intent( + parent="parent_value", intent=gcdc_intent.Intent(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].intent == gcdc_intent.Intent(name="name_value") + + +@pytest.mark.asyncio +async def test_create_intent_flattened_error_async(): + client = IntentsAsyncClient(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_intent( + gcdc_intent.CreateIntentRequest(), + parent="parent_value", + intent=gcdc_intent.Intent(name="name_value"), + ) + + +def test_update_intent( + transport: str = "grpc", request_type=gcdc_intent.UpdateIntentRequest +): + client = IntentsClient( + 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_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_intent.Intent( + name="name_value", + display_name="display_name_value", + priority=898, + is_fallback=True, + description="description_value", + ) + + response = client.update_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_intent.UpdateIntentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_intent.Intent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.priority == 898 + + assert response.is_fallback is True + + assert response.description == "description_value" + + +def test_update_intent_from_dict(): + test_update_intent(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_intent_async( + transport: str = "grpc_asyncio", request_type=gcdc_intent.UpdateIntentRequest +): + client = IntentsAsyncClient( + 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_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_intent.Intent( + name="name_value", + display_name="display_name_value", + priority=898, + is_fallback=True, + description="description_value", + ) + ) + + response = await client.update_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_intent.UpdateIntentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_intent.Intent) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.priority == 898 + + assert response.is_fallback is True + + assert response.description == "description_value" + + +@pytest.mark.asyncio +async def test_update_intent_async_from_dict(): + await test_update_intent_async(request_type=dict) + + +def test_update_intent_field_headers(): + client = IntentsClient(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 = gcdc_intent.UpdateIntentRequest() + request.intent.name = "intent.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: + call.return_value = gcdc_intent.Intent() + + client.update_intent(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", "intent.name=intent.name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_intent_field_headers_async(): + client = IntentsAsyncClient(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 = gcdc_intent.UpdateIntentRequest() + request.intent.name = "intent.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_intent.Intent()) + + await client.update_intent(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", "intent.name=intent.name/value",) in kw["metadata"] + + +def test_update_intent_flattened(): + client = IntentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_intent.Intent() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_intent( + intent=gcdc_intent.Intent(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].intent == gcdc_intent.Intent(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_intent_flattened_error(): + client = IntentsClient(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_intent( + gcdc_intent.UpdateIntentRequest(), + intent=gcdc_intent.Intent(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_intent_flattened_async(): + client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_intent.Intent() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_intent.Intent()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_intent( + intent=gcdc_intent.Intent(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].intent == gcdc_intent.Intent(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_intent_flattened_error_async(): + client = IntentsAsyncClient(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_intent( + gcdc_intent.UpdateIntentRequest(), + intent=gcdc_intent.Intent(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_intent( + transport: str = "grpc", request_type=intent.DeleteIntentRequest +): + client = IntentsClient( + 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_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.DeleteIntentRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_intent_from_dict(): + test_delete_intent(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_intent_async( + transport: str = "grpc_asyncio", request_type=intent.DeleteIntentRequest +): + client = IntentsAsyncClient( + 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_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.DeleteIntentRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_intent_async_from_dict(): + await test_delete_intent_async(request_type=dict) + + +def test_delete_intent_field_headers(): + client = IntentsClient(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 = intent.DeleteIntentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_intent), "__call__") as call: + call.return_value = None + + client.delete_intent(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_intent_field_headers_async(): + client = IntentsAsyncClient(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 = intent.DeleteIntentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_intent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_intent(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_intent_flattened(): + client = IntentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_intent), "__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_intent(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_intent_flattened_error(): + client = IntentsClient(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_intent( + intent.DeleteIntentRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_intent_flattened_async(): + client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_intent), "__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_intent(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_intent_flattened_error_async(): + client = IntentsAsyncClient(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_intent( + intent.DeleteIntentRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.IntentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.IntentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = IntentsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.IntentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = IntentsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.IntentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = IntentsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.IntentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.IntentsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], +) +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 = IntentsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.IntentsGrpcTransport,) + + +def test_intents_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.IntentsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_intents_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.intents.transports.IntentsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.IntentsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_intents", + "get_intent", + "create_intent", + "update_intent", + "delete_intent", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_intents_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.dialogflowcx_v3.services.intents.transports.IntentsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.IntentsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_intents_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.dialogflowcx_v3.services.intents.transports.IntentsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.IntentsTransport() + adc.assert_called_once() + + +def test_intents_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) + IntentsClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_intents_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.IntentsGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_intents_host_no_port(): + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_intents_host_with_port(): + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_intents_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.IntentsGrpcTransport( + 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_intents_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.IntentsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], +) +def test_intents_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], +) +def test_intents_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_entity_type_path(): + project = "squid" + location = "clam" + agent = "whelk" + entity_type = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + actual = IntentsClient.entity_type_path(project, location, agent, entity_type) + assert expected == actual + + +def test_parse_entity_type_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "entity_type": "mussel", + } + path = IntentsClient.entity_type_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_entity_type_path(path) + assert expected == actual + + +def test_intent_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + intent = "abalone" + + expected = "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + actual = IntentsClient.intent_path(project, location, agent, intent) + assert expected == actual + + +def test_parse_intent_path(): + expected = { + "project": "squid", + "location": "clam", + "agent": "whelk", + "intent": "octopus", + } + path = IntentsClient.intent_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_intent_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "oyster" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = IntentsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nudibranch", + } + path = IntentsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "cuttlefish" + + expected = "folders/{folder}".format(folder=folder,) + actual = IntentsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "mussel", + } + path = IntentsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "winkle" + + expected = "organizations/{organization}".format(organization=organization,) + actual = IntentsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nautilus", + } + path = IntentsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "scallop" + + expected = "projects/{project}".format(project=project,) + actual = IntentsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "abalone", + } + path = IntentsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "squid" + location = "clam" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = IntentsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "whelk", + "location": "octopus", + } + path = IntentsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.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.IntentsTransport, "_prep_wrapped_messages" + ) as prep: + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.IntentsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = IntentsClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3/test_pages.py b/tests/unit/gapic/dialogflowcx_v3/test_pages.py new file mode 100644 index 00000000..11479877 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_pages.py @@ -0,0 +1,2097 @@ +# -*- 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.dialogflowcx_v3.services.pages import PagesAsyncClient +from google.cloud.dialogflowcx_v3.services.pages import PagesClient +from google.cloud.dialogflowcx_v3.services.pages import pagers +from google.cloud.dialogflowcx_v3.services.pages import transports +from google.cloud.dialogflowcx_v3.types import fulfillment +from google.cloud.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import page as gcdc_page +from google.cloud.dialogflowcx_v3.types import response_message +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # 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 PagesClient._get_default_mtls_endpoint(None) is None + assert PagesClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + PagesClient._get_default_mtls_endpoint(api_mtls_endpoint) == api_mtls_endpoint + ) + assert ( + PagesClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + PagesClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert PagesClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [PagesClient, PagesAsyncClient]) +def test_pages_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 == "dialogflow.googleapis.com:443" + + +def test_pages_client_get_transport_class(): + transport = PagesClient.get_transport_class() + assert transport == transports.PagesGrpcTransport + + transport = PagesClient.get_transport_class("grpc") + assert transport == transports.PagesGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (PagesClient, transports.PagesGrpcTransport, "grpc"), + (PagesAsyncClient, transports.PagesGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +@mock.patch.object( + PagesClient, "DEFAULT_ENDPOINT", modify_default_endpoint(PagesClient) +) +@mock.patch.object( + PagesAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(PagesAsyncClient) +) +def test_pages_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(PagesClient, "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(PagesClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + (PagesClient, transports.PagesGrpcTransport, "grpc", "true"), + ( + PagesAsyncClient, + transports.PagesGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (PagesClient, transports.PagesGrpcTransport, "grpc", "false"), + ( + PagesAsyncClient, + transports.PagesGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + PagesClient, "DEFAULT_ENDPOINT", modify_default_endpoint(PagesClient) +) +@mock.patch.object( + PagesAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(PagesAsyncClient) +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_pages_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (PagesClient, transports.PagesGrpcTransport, "grpc"), + (PagesAsyncClient, transports.PagesGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_pages_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (PagesClient, transports.PagesGrpcTransport, "grpc"), + (PagesAsyncClient, transports.PagesGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_pages_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_pages_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.pages.transports.PagesGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = PagesClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_pages(transport: str = "grpc", request_type=page.ListPagesRequest): + client = PagesClient( + 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_pages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = page.ListPagesResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_pages(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == page.ListPagesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListPagesPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_pages_from_dict(): + test_list_pages(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_pages_async( + transport: str = "grpc_asyncio", request_type=page.ListPagesRequest +): + client = PagesAsyncClient( + 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_pages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + page.ListPagesResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_pages(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == page.ListPagesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListPagesAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_pages_async_from_dict(): + await test_list_pages_async(request_type=dict) + + +def test_list_pages_field_headers(): + client = PagesClient(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 = page.ListPagesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: + call.return_value = page.ListPagesResponse() + + client.list_pages(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_pages_field_headers_async(): + client = PagesAsyncClient(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 = page.ListPagesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + page.ListPagesResponse() + ) + + await client.list_pages(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_pages_flattened(): + client = PagesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = page.ListPagesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_pages(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_pages_flattened_error(): + client = PagesClient(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_pages( + page.ListPagesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_pages_flattened_async(): + client = PagesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = page.ListPagesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + page.ListPagesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_pages(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_pages_flattened_error_async(): + client = PagesAsyncClient(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_pages( + page.ListPagesRequest(), parent="parent_value", + ) + + +def test_list_pages_pager(): + client = PagesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + page.ListPagesResponse( + pages=[page.Page(), page.Page(), page.Page(),], next_page_token="abc", + ), + page.ListPagesResponse(pages=[], next_page_token="def",), + page.ListPagesResponse(pages=[page.Page(),], next_page_token="ghi",), + page.ListPagesResponse(pages=[page.Page(), page.Page(),],), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_pages(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, page.Page) for i in results) + + +def test_list_pages_pages(): + client = PagesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + page.ListPagesResponse( + pages=[page.Page(), page.Page(), page.Page(),], next_page_token="abc", + ), + page.ListPagesResponse(pages=[], next_page_token="def",), + page.ListPagesResponse(pages=[page.Page(),], next_page_token="ghi",), + page.ListPagesResponse(pages=[page.Page(), page.Page(),],), + RuntimeError, + ) + pages = list(client.list_pages(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_pages_async_pager(): + client = PagesAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_pages), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + page.ListPagesResponse( + pages=[page.Page(), page.Page(), page.Page(),], next_page_token="abc", + ), + page.ListPagesResponse(pages=[], next_page_token="def",), + page.ListPagesResponse(pages=[page.Page(),], next_page_token="ghi",), + page.ListPagesResponse(pages=[page.Page(), page.Page(),],), + RuntimeError, + ) + async_pager = await client.list_pages(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, page.Page) for i in responses) + + +@pytest.mark.asyncio +async def test_list_pages_async_pages(): + client = PagesAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_pages), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + page.ListPagesResponse( + pages=[page.Page(), page.Page(), page.Page(),], next_page_token="abc", + ), + page.ListPagesResponse(pages=[], next_page_token="def",), + page.ListPagesResponse(pages=[page.Page(),], next_page_token="ghi",), + page.ListPagesResponse(pages=[page.Page(), page.Page(),],), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_pages(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_page(transport: str = "grpc", request_type=page.GetPageRequest): + client = PagesClient( + 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_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = page.Page( + name="name_value", + display_name="display_name_value", + transition_route_groups=["transition_route_groups_value"], + ) + + response = client.get_page(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == page.GetPageRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, page.Page) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.transition_route_groups == ["transition_route_groups_value"] + + +def test_get_page_from_dict(): + test_get_page(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_page_async( + transport: str = "grpc_asyncio", request_type=page.GetPageRequest +): + client = PagesAsyncClient( + 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_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + page.Page( + name="name_value", + display_name="display_name_value", + transition_route_groups=["transition_route_groups_value"], + ) + ) + + response = await client.get_page(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == page.GetPageRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, page.Page) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.transition_route_groups == ["transition_route_groups_value"] + + +@pytest.mark.asyncio +async def test_get_page_async_from_dict(): + await test_get_page_async(request_type=dict) + + +def test_get_page_field_headers(): + client = PagesClient(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 = page.GetPageRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_page), "__call__") as call: + call.return_value = page.Page() + + client.get_page(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_page_field_headers_async(): + client = PagesAsyncClient(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 = page.GetPageRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_page), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(page.Page()) + + await client.get_page(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_page_flattened(): + client = PagesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = page.Page() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_page(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_page_flattened_error(): + client = PagesClient(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_page( + page.GetPageRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_page_flattened_async(): + client = PagesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = page.Page() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(page.Page()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_page(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_page_flattened_error_async(): + client = PagesAsyncClient(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_page( + page.GetPageRequest(), name="name_value", + ) + + +def test_create_page(transport: str = "grpc", request_type=gcdc_page.CreatePageRequest): + client = PagesClient( + 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_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_page.Page( + name="name_value", + display_name="display_name_value", + transition_route_groups=["transition_route_groups_value"], + ) + + response = client.create_page(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_page.CreatePageRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_page.Page) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.transition_route_groups == ["transition_route_groups_value"] + + +def test_create_page_from_dict(): + test_create_page(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_page_async( + transport: str = "grpc_asyncio", request_type=gcdc_page.CreatePageRequest +): + client = PagesAsyncClient( + 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_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_page.Page( + name="name_value", + display_name="display_name_value", + transition_route_groups=["transition_route_groups_value"], + ) + ) + + response = await client.create_page(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_page.CreatePageRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_page.Page) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.transition_route_groups == ["transition_route_groups_value"] + + +@pytest.mark.asyncio +async def test_create_page_async_from_dict(): + await test_create_page_async(request_type=dict) + + +def test_create_page_field_headers(): + client = PagesClient(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 = gcdc_page.CreatePageRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_page), "__call__") as call: + call.return_value = gcdc_page.Page() + + client.create_page(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_page_field_headers_async(): + client = PagesAsyncClient(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 = gcdc_page.CreatePageRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_page), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_page.Page()) + + await client.create_page(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_page_flattened(): + client = PagesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_page.Page() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_page( + parent="parent_value", page=gcdc_page.Page(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].page == gcdc_page.Page(name="name_value") + + +def test_create_page_flattened_error(): + client = PagesClient(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_page( + gcdc_page.CreatePageRequest(), + parent="parent_value", + page=gcdc_page.Page(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_page_flattened_async(): + client = PagesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_page.Page() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_page.Page()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_page( + parent="parent_value", page=gcdc_page.Page(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].page == gcdc_page.Page(name="name_value") + + +@pytest.mark.asyncio +async def test_create_page_flattened_error_async(): + client = PagesAsyncClient(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_page( + gcdc_page.CreatePageRequest(), + parent="parent_value", + page=gcdc_page.Page(name="name_value"), + ) + + +def test_update_page(transport: str = "grpc", request_type=gcdc_page.UpdatePageRequest): + client = PagesClient( + 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_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_page.Page( + name="name_value", + display_name="display_name_value", + transition_route_groups=["transition_route_groups_value"], + ) + + response = client.update_page(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_page.UpdatePageRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_page.Page) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.transition_route_groups == ["transition_route_groups_value"] + + +def test_update_page_from_dict(): + test_update_page(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_page_async( + transport: str = "grpc_asyncio", request_type=gcdc_page.UpdatePageRequest +): + client = PagesAsyncClient( + 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_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_page.Page( + name="name_value", + display_name="display_name_value", + transition_route_groups=["transition_route_groups_value"], + ) + ) + + response = await client.update_page(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_page.UpdatePageRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_page.Page) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.transition_route_groups == ["transition_route_groups_value"] + + +@pytest.mark.asyncio +async def test_update_page_async_from_dict(): + await test_update_page_async(request_type=dict) + + +def test_update_page_field_headers(): + client = PagesClient(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 = gcdc_page.UpdatePageRequest() + request.page.name = "page.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_page), "__call__") as call: + call.return_value = gcdc_page.Page() + + client.update_page(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", "page.name=page.name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_page_field_headers_async(): + client = PagesAsyncClient(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 = gcdc_page.UpdatePageRequest() + request.page.name = "page.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_page), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_page.Page()) + + await client.update_page(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", "page.name=page.name/value",) in kw["metadata"] + + +def test_update_page_flattened(): + client = PagesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_page.Page() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_page( + page=gcdc_page.Page(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].page == gcdc_page.Page(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_page_flattened_error(): + client = PagesClient(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_page( + gcdc_page.UpdatePageRequest(), + page=gcdc_page.Page(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_page_flattened_async(): + client = PagesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_page.Page() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_page.Page()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_page( + page=gcdc_page.Page(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].page == gcdc_page.Page(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_page_flattened_error_async(): + client = PagesAsyncClient(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_page( + gcdc_page.UpdatePageRequest(), + page=gcdc_page.Page(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_page(transport: str = "grpc", request_type=page.DeletePageRequest): + client = PagesClient( + 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_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_page(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == page.DeletePageRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_page_from_dict(): + test_delete_page(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_page_async( + transport: str = "grpc_asyncio", request_type=page.DeletePageRequest +): + client = PagesAsyncClient( + 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_page), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_page(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == page.DeletePageRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_page_async_from_dict(): + await test_delete_page_async(request_type=dict) + + +def test_delete_page_field_headers(): + client = PagesClient(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 = page.DeletePageRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_page), "__call__") as call: + call.return_value = None + + client.delete_page(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_page_field_headers_async(): + client = PagesAsyncClient(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 = page.DeletePageRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_page), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_page(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_page_flattened(): + client = PagesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_page), "__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_page(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_page_flattened_error(): + client = PagesClient(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_page( + page.DeletePageRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_page_flattened_async(): + client = PagesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_page), "__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_page(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_page_flattened_error_async(): + client = PagesAsyncClient(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_page( + page.DeletePageRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.PagesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = PagesClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.PagesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = PagesClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.PagesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = PagesClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.PagesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = PagesClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.PagesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.PagesGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.PagesGrpcTransport, transports.PagesGrpcAsyncIOTransport], +) +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 = PagesClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.PagesGrpcTransport,) + + +def test_pages_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.PagesTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_pages_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.pages.transports.PagesTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.PagesTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_pages", + "get_page", + "create_page", + "update_page", + "delete_page", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_pages_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.dialogflowcx_v3.services.pages.transports.PagesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.PagesTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_pages_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.dialogflowcx_v3.services.pages.transports.PagesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.PagesTransport() + adc.assert_called_once() + + +def test_pages_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) + PagesClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_pages_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.PagesGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_pages_host_no_port(): + client = PagesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_pages_host_with_port(): + client = PagesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_pages_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.PagesGrpcTransport(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_pages_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.PagesGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [transports.PagesGrpcTransport, transports.PagesGrpcAsyncIOTransport], +) +def test_pages_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [transports.PagesGrpcTransport, transports.PagesGrpcAsyncIOTransport], +) +def test_pages_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_entity_type_path(): + project = "squid" + location = "clam" + agent = "whelk" + entity_type = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + actual = PagesClient.entity_type_path(project, location, agent, entity_type) + assert expected == actual + + +def test_parse_entity_type_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "entity_type": "mussel", + } + path = PagesClient.entity_type_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_entity_type_path(path) + assert expected == actual + + +def test_flow_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + flow = "abalone" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + actual = PagesClient.flow_path(project, location, agent, flow) + assert expected == actual + + +def test_parse_flow_path(): + expected = { + "project": "squid", + "location": "clam", + "agent": "whelk", + "flow": "octopus", + } + path = PagesClient.flow_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_flow_path(path) + assert expected == actual + + +def test_intent_path(): + project = "oyster" + location = "nudibranch" + agent = "cuttlefish" + intent = "mussel" + + expected = "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + actual = PagesClient.intent_path(project, location, agent, intent) + assert expected == actual + + +def test_parse_intent_path(): + expected = { + "project": "winkle", + "location": "nautilus", + "agent": "scallop", + "intent": "abalone", + } + path = PagesClient.intent_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_intent_path(path) + assert expected == actual + + +def test_page_path(): + project = "squid" + location = "clam" + agent = "whelk" + flow = "octopus" + page = "oyster" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + actual = PagesClient.page_path(project, location, agent, flow, page) + assert expected == actual + + +def test_parse_page_path(): + expected = { + "project": "nudibranch", + "location": "cuttlefish", + "agent": "mussel", + "flow": "winkle", + "page": "nautilus", + } + path = PagesClient.page_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_page_path(path) + assert expected == actual + + +def test_transition_route_group_path(): + project = "scallop" + location = "abalone" + agent = "squid" + flow = "clam" + transition_route_group = "whelk" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/transitionRouteGroups/{transition_route_group}".format( + project=project, + location=location, + agent=agent, + flow=flow, + transition_route_group=transition_route_group, + ) + actual = PagesClient.transition_route_group_path( + project, location, agent, flow, transition_route_group + ) + assert expected == actual + + +def test_parse_transition_route_group_path(): + expected = { + "project": "octopus", + "location": "oyster", + "agent": "nudibranch", + "flow": "cuttlefish", + "transition_route_group": "mussel", + } + path = PagesClient.transition_route_group_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_transition_route_group_path(path) + assert expected == actual + + +def test_webhook_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + webhook = "abalone" + + expected = "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + actual = PagesClient.webhook_path(project, location, agent, webhook) + assert expected == actual + + +def test_parse_webhook_path(): + expected = { + "project": "squid", + "location": "clam", + "agent": "whelk", + "webhook": "octopus", + } + path = PagesClient.webhook_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_webhook_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "oyster" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = PagesClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nudibranch", + } + path = PagesClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "cuttlefish" + + expected = "folders/{folder}".format(folder=folder,) + actual = PagesClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "mussel", + } + path = PagesClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "winkle" + + expected = "organizations/{organization}".format(organization=organization,) + actual = PagesClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nautilus", + } + path = PagesClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "scallop" + + expected = "projects/{project}".format(project=project,) + actual = PagesClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "abalone", + } + path = PagesClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "squid" + location = "clam" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = PagesClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "whelk", + "location": "octopus", + } + path = PagesClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.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.PagesTransport, "_prep_wrapped_messages") as prep: + client = PagesClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object(transports.PagesTransport, "_prep_wrapped_messages") as prep: + transport_class = PagesClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3/test_security_settings_service.py b/tests/unit/gapic/dialogflowcx_v3/test_security_settings_service.py new file mode 100644 index 00000000..d7ae1702 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_security_settings_service.py @@ -0,0 +1,2408 @@ +# -*- 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.dialogflowcx_v3.services.security_settings_service import ( + SecuritySettingsServiceAsyncClient, +) +from google.cloud.dialogflowcx_v3.services.security_settings_service import ( + SecuritySettingsServiceClient, +) +from google.cloud.dialogflowcx_v3.services.security_settings_service import pagers +from google.cloud.dialogflowcx_v3.services.security_settings_service import transports +from google.cloud.dialogflowcx_v3.types import security_settings +from google.cloud.dialogflowcx_v3.types import ( + security_settings as gcdc_security_settings, +) +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # 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 SecuritySettingsServiceClient._get_default_mtls_endpoint(None) is None + assert ( + SecuritySettingsServiceClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + SecuritySettingsServiceClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + SecuritySettingsServiceClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + SecuritySettingsServiceClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + SecuritySettingsServiceClient._get_default_mtls_endpoint(non_googleapi) + == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [SecuritySettingsServiceClient, SecuritySettingsServiceAsyncClient] +) +def test_security_settings_service_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 == "dialogflow.googleapis.com:443" + + +def test_security_settings_service_client_get_transport_class(): + transport = SecuritySettingsServiceClient.get_transport_class() + assert transport == transports.SecuritySettingsServiceGrpcTransport + + transport = SecuritySettingsServiceClient.get_transport_class("grpc") + assert transport == transports.SecuritySettingsServiceGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + SecuritySettingsServiceClient, + transports.SecuritySettingsServiceGrpcTransport, + "grpc", + ), + ( + SecuritySettingsServiceAsyncClient, + transports.SecuritySettingsServiceGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + SecuritySettingsServiceClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SecuritySettingsServiceClient), +) +@mock.patch.object( + SecuritySettingsServiceAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SecuritySettingsServiceAsyncClient), +) +def test_security_settings_service_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(SecuritySettingsServiceClient, "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(SecuritySettingsServiceClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + ( + SecuritySettingsServiceClient, + transports.SecuritySettingsServiceGrpcTransport, + "grpc", + "true", + ), + ( + SecuritySettingsServiceAsyncClient, + transports.SecuritySettingsServiceGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + ( + SecuritySettingsServiceClient, + transports.SecuritySettingsServiceGrpcTransport, + "grpc", + "false", + ), + ( + SecuritySettingsServiceAsyncClient, + transports.SecuritySettingsServiceGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + SecuritySettingsServiceClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SecuritySettingsServiceClient), +) +@mock.patch.object( + SecuritySettingsServiceAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SecuritySettingsServiceAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_security_settings_service_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + SecuritySettingsServiceClient, + transports.SecuritySettingsServiceGrpcTransport, + "grpc", + ), + ( + SecuritySettingsServiceAsyncClient, + transports.SecuritySettingsServiceGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_security_settings_service_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + SecuritySettingsServiceClient, + transports.SecuritySettingsServiceGrpcTransport, + "grpc", + ), + ( + SecuritySettingsServiceAsyncClient, + transports.SecuritySettingsServiceGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_security_settings_service_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_security_settings_service_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.security_settings_service.transports.SecuritySettingsServiceGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = SecuritySettingsServiceClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_create_security_settings( + transport: str = "grpc", + request_type=gcdc_security_settings.CreateSecuritySettingsRequest, +): + client = SecuritySettingsServiceClient( + 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_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_security_settings.SecuritySettings( + name="name_value", + display_name="display_name_value", + redaction_strategy=gcdc_security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE, + redaction_scope=gcdc_security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE, + inspect_template="inspect_template_value", + purge_data_types=[ + gcdc_security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ], + retention_window_days=2271, + ) + + response = client.create_security_settings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_security_settings.CreateSecuritySettingsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_security_settings.SecuritySettings) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert ( + response.redaction_strategy + == gcdc_security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE + ) + + assert ( + response.redaction_scope + == gcdc_security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE + ) + + assert response.inspect_template == "inspect_template_value" + + assert response.purge_data_types == [ + gcdc_security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ] + + +def test_create_security_settings_from_dict(): + test_create_security_settings(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_security_settings_async( + transport: str = "grpc_asyncio", + request_type=gcdc_security_settings.CreateSecuritySettingsRequest, +): + client = SecuritySettingsServiceAsyncClient( + 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_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_security_settings.SecuritySettings( + name="name_value", + display_name="display_name_value", + redaction_strategy=gcdc_security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE, + redaction_scope=gcdc_security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE, + inspect_template="inspect_template_value", + purge_data_types=[ + gcdc_security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ], + ) + ) + + response = await client.create_security_settings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_security_settings.CreateSecuritySettingsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_security_settings.SecuritySettings) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert ( + response.redaction_strategy + == gcdc_security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE + ) + + assert ( + response.redaction_scope + == gcdc_security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE + ) + + assert response.inspect_template == "inspect_template_value" + + assert response.purge_data_types == [ + gcdc_security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ] + + +@pytest.mark.asyncio +async def test_create_security_settings_async_from_dict(): + await test_create_security_settings_async(request_type=dict) + + +def test_create_security_settings_field_headers(): + client = SecuritySettingsServiceClient( + 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 = gcdc_security_settings.CreateSecuritySettingsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_security_settings), "__call__" + ) as call: + call.return_value = gcdc_security_settings.SecuritySettings() + + client.create_security_settings(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_security_settings_field_headers_async(): + client = SecuritySettingsServiceAsyncClient( + 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 = gcdc_security_settings.CreateSecuritySettingsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_security_settings), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_security_settings.SecuritySettings() + ) + + await client.create_security_settings(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_security_settings_flattened(): + client = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_security_settings.SecuritySettings() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_security_settings( + parent="parent_value", + security_settings=gcdc_security_settings.SecuritySettings( + 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].security_settings == gcdc_security_settings.SecuritySettings( + name="name_value" + ) + + +def test_create_security_settings_flattened_error(): + client = SecuritySettingsServiceClient( + 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_security_settings( + gcdc_security_settings.CreateSecuritySettingsRequest(), + parent="parent_value", + security_settings=gcdc_security_settings.SecuritySettings( + name="name_value" + ), + ) + + +@pytest.mark.asyncio +async def test_create_security_settings_flattened_async(): + client = SecuritySettingsServiceAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_security_settings.SecuritySettings() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_security_settings.SecuritySettings() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_security_settings( + parent="parent_value", + security_settings=gcdc_security_settings.SecuritySettings( + 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].security_settings == gcdc_security_settings.SecuritySettings( + name="name_value" + ) + + +@pytest.mark.asyncio +async def test_create_security_settings_flattened_error_async(): + client = SecuritySettingsServiceAsyncClient( + 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_security_settings( + gcdc_security_settings.CreateSecuritySettingsRequest(), + parent="parent_value", + security_settings=gcdc_security_settings.SecuritySettings( + name="name_value" + ), + ) + + +def test_get_security_settings( + transport: str = "grpc", request_type=security_settings.GetSecuritySettingsRequest +): + client = SecuritySettingsServiceClient( + 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_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = security_settings.SecuritySettings( + name="name_value", + display_name="display_name_value", + redaction_strategy=security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE, + redaction_scope=security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE, + inspect_template="inspect_template_value", + purge_data_types=[ + security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ], + retention_window_days=2271, + ) + + response = client.get_security_settings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == security_settings.GetSecuritySettingsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, security_settings.SecuritySettings) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert ( + response.redaction_strategy + == security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE + ) + + assert ( + response.redaction_scope + == security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE + ) + + assert response.inspect_template == "inspect_template_value" + + assert response.purge_data_types == [ + security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ] + + +def test_get_security_settings_from_dict(): + test_get_security_settings(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_security_settings_async( + transport: str = "grpc_asyncio", + request_type=security_settings.GetSecuritySettingsRequest, +): + client = SecuritySettingsServiceAsyncClient( + 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_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + security_settings.SecuritySettings( + name="name_value", + display_name="display_name_value", + redaction_strategy=security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE, + redaction_scope=security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE, + inspect_template="inspect_template_value", + purge_data_types=[ + security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ], + ) + ) + + response = await client.get_security_settings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == security_settings.GetSecuritySettingsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, security_settings.SecuritySettings) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert ( + response.redaction_strategy + == security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE + ) + + assert ( + response.redaction_scope + == security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE + ) + + assert response.inspect_template == "inspect_template_value" + + assert response.purge_data_types == [ + security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ] + + +@pytest.mark.asyncio +async def test_get_security_settings_async_from_dict(): + await test_get_security_settings_async(request_type=dict) + + +def test_get_security_settings_field_headers(): + client = SecuritySettingsServiceClient( + 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 = security_settings.GetSecuritySettingsRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_security_settings), "__call__" + ) as call: + call.return_value = security_settings.SecuritySettings() + + client.get_security_settings(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_security_settings_field_headers_async(): + client = SecuritySettingsServiceAsyncClient( + 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 = security_settings.GetSecuritySettingsRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_security_settings), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + security_settings.SecuritySettings() + ) + + await client.get_security_settings(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_security_settings_flattened(): + client = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = security_settings.SecuritySettings() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_security_settings(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_security_settings_flattened_error(): + client = SecuritySettingsServiceClient( + 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_security_settings( + security_settings.GetSecuritySettingsRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_security_settings_flattened_async(): + client = SecuritySettingsServiceAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = security_settings.SecuritySettings() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + security_settings.SecuritySettings() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_security_settings(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_security_settings_flattened_error_async(): + client = SecuritySettingsServiceAsyncClient( + 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_security_settings( + security_settings.GetSecuritySettingsRequest(), name="name_value", + ) + + +def test_update_security_settings( + transport: str = "grpc", + request_type=gcdc_security_settings.UpdateSecuritySettingsRequest, +): + client = SecuritySettingsServiceClient( + 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_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_security_settings.SecuritySettings( + name="name_value", + display_name="display_name_value", + redaction_strategy=gcdc_security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE, + redaction_scope=gcdc_security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE, + inspect_template="inspect_template_value", + purge_data_types=[ + gcdc_security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ], + retention_window_days=2271, + ) + + response = client.update_security_settings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_security_settings.UpdateSecuritySettingsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_security_settings.SecuritySettings) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert ( + response.redaction_strategy + == gcdc_security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE + ) + + assert ( + response.redaction_scope + == gcdc_security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE + ) + + assert response.inspect_template == "inspect_template_value" + + assert response.purge_data_types == [ + gcdc_security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ] + + +def test_update_security_settings_from_dict(): + test_update_security_settings(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_security_settings_async( + transport: str = "grpc_asyncio", + request_type=gcdc_security_settings.UpdateSecuritySettingsRequest, +): + client = SecuritySettingsServiceAsyncClient( + 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_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_security_settings.SecuritySettings( + name="name_value", + display_name="display_name_value", + redaction_strategy=gcdc_security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE, + redaction_scope=gcdc_security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE, + inspect_template="inspect_template_value", + purge_data_types=[ + gcdc_security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ], + ) + ) + + response = await client.update_security_settings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_security_settings.UpdateSecuritySettingsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_security_settings.SecuritySettings) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert ( + response.redaction_strategy + == gcdc_security_settings.SecuritySettings.RedactionStrategy.REDACT_WITH_SERVICE + ) + + assert ( + response.redaction_scope + == gcdc_security_settings.SecuritySettings.RedactionScope.REDACT_DISK_STORAGE + ) + + assert response.inspect_template == "inspect_template_value" + + assert response.purge_data_types == [ + gcdc_security_settings.SecuritySettings.PurgeDataType.DIALOGFLOW_HISTORY + ] + + +@pytest.mark.asyncio +async def test_update_security_settings_async_from_dict(): + await test_update_security_settings_async(request_type=dict) + + +def test_update_security_settings_field_headers(): + client = SecuritySettingsServiceClient( + 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 = gcdc_security_settings.UpdateSecuritySettingsRequest() + request.security_settings.name = "security_settings.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_security_settings), "__call__" + ) as call: + call.return_value = gcdc_security_settings.SecuritySettings() + + client.update_security_settings(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", + "security_settings.name=security_settings.name/value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_security_settings_field_headers_async(): + client = SecuritySettingsServiceAsyncClient( + 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 = gcdc_security_settings.UpdateSecuritySettingsRequest() + request.security_settings.name = "security_settings.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_security_settings), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_security_settings.SecuritySettings() + ) + + await client.update_security_settings(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", + "security_settings.name=security_settings.name/value", + ) in kw["metadata"] + + +def test_update_security_settings_flattened(): + client = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_security_settings.SecuritySettings() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_security_settings( + security_settings=gcdc_security_settings.SecuritySettings( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].security_settings == gcdc_security_settings.SecuritySettings( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_security_settings_flattened_error(): + client = SecuritySettingsServiceClient( + 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_security_settings( + gcdc_security_settings.UpdateSecuritySettingsRequest(), + security_settings=gcdc_security_settings.SecuritySettings( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_security_settings_flattened_async(): + client = SecuritySettingsServiceAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_security_settings.SecuritySettings() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_security_settings.SecuritySettings() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_security_settings( + security_settings=gcdc_security_settings.SecuritySettings( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].security_settings == gcdc_security_settings.SecuritySettings( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_security_settings_flattened_error_async(): + client = SecuritySettingsServiceAsyncClient( + 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_security_settings( + gcdc_security_settings.UpdateSecuritySettingsRequest(), + security_settings=gcdc_security_settings.SecuritySettings( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_list_security_settings( + transport: str = "grpc", request_type=security_settings.ListSecuritySettingsRequest +): + client = SecuritySettingsServiceClient( + 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_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = security_settings.ListSecuritySettingsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_security_settings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == security_settings.ListSecuritySettingsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListSecuritySettingsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_security_settings_from_dict(): + test_list_security_settings(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_security_settings_async( + transport: str = "grpc_asyncio", + request_type=security_settings.ListSecuritySettingsRequest, +): + client = SecuritySettingsServiceAsyncClient( + 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_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + security_settings.ListSecuritySettingsResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_security_settings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == security_settings.ListSecuritySettingsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListSecuritySettingsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_security_settings_async_from_dict(): + await test_list_security_settings_async(request_type=dict) + + +def test_list_security_settings_field_headers(): + client = SecuritySettingsServiceClient( + 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 = security_settings.ListSecuritySettingsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_security_settings), "__call__" + ) as call: + call.return_value = security_settings.ListSecuritySettingsResponse() + + client.list_security_settings(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_security_settings_field_headers_async(): + client = SecuritySettingsServiceAsyncClient( + 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 = security_settings.ListSecuritySettingsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_security_settings), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + security_settings.ListSecuritySettingsResponse() + ) + + await client.list_security_settings(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_security_settings_flattened(): + client = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = security_settings.ListSecuritySettingsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_security_settings(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_security_settings_flattened_error(): + client = SecuritySettingsServiceClient( + 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_security_settings( + security_settings.ListSecuritySettingsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_security_settings_flattened_async(): + client = SecuritySettingsServiceAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = security_settings.ListSecuritySettingsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + security_settings.ListSecuritySettingsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_security_settings(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_security_settings_flattened_error_async(): + client = SecuritySettingsServiceAsyncClient( + 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_security_settings( + security_settings.ListSecuritySettingsRequest(), parent="parent_value", + ) + + +def test_list_security_settings_pager(): + client = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_security_settings), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + security_settings.ListSecuritySettingsResponse( + security_settings=[ + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + ], + next_page_token="abc", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[], next_page_token="def", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[security_settings.SecuritySettings(),], + next_page_token="ghi", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[ + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + ], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_security_settings(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, security_settings.SecuritySettings) for i in results) + + +def test_list_security_settings_pages(): + client = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_security_settings), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + security_settings.ListSecuritySettingsResponse( + security_settings=[ + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + ], + next_page_token="abc", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[], next_page_token="def", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[security_settings.SecuritySettings(),], + next_page_token="ghi", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[ + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + ], + ), + RuntimeError, + ) + pages = list(client.list_security_settings(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_security_settings_async_pager(): + client = SecuritySettingsServiceAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_security_settings), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + security_settings.ListSecuritySettingsResponse( + security_settings=[ + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + ], + next_page_token="abc", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[], next_page_token="def", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[security_settings.SecuritySettings(),], + next_page_token="ghi", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[ + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + ], + ), + RuntimeError, + ) + async_pager = await client.list_security_settings(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, security_settings.SecuritySettings) for i in responses) + + +@pytest.mark.asyncio +async def test_list_security_settings_async_pages(): + client = SecuritySettingsServiceAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_security_settings), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + security_settings.ListSecuritySettingsResponse( + security_settings=[ + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + ], + next_page_token="abc", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[], next_page_token="def", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[security_settings.SecuritySettings(),], + next_page_token="ghi", + ), + security_settings.ListSecuritySettingsResponse( + security_settings=[ + security_settings.SecuritySettings(), + security_settings.SecuritySettings(), + ], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_security_settings(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_delete_security_settings( + transport: str = "grpc", + request_type=security_settings.DeleteSecuritySettingsRequest, +): + client = SecuritySettingsServiceClient( + 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_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_security_settings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == security_settings.DeleteSecuritySettingsRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_security_settings_from_dict(): + test_delete_security_settings(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_security_settings_async( + transport: str = "grpc_asyncio", + request_type=security_settings.DeleteSecuritySettingsRequest, +): + client = SecuritySettingsServiceAsyncClient( + 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_security_settings), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_security_settings(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == security_settings.DeleteSecuritySettingsRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_security_settings_async_from_dict(): + await test_delete_security_settings_async(request_type=dict) + + +def test_delete_security_settings_field_headers(): + client = SecuritySettingsServiceClient( + 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 = security_settings.DeleteSecuritySettingsRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_security_settings), "__call__" + ) as call: + call.return_value = None + + client.delete_security_settings(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_security_settings_field_headers_async(): + client = SecuritySettingsServiceAsyncClient( + 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 = security_settings.DeleteSecuritySettingsRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_security_settings), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_security_settings(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_security_settings_flattened(): + client = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_security_settings), "__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_security_settings(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_security_settings_flattened_error(): + client = SecuritySettingsServiceClient( + 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_security_settings( + security_settings.DeleteSecuritySettingsRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_security_settings_flattened_async(): + client = SecuritySettingsServiceAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_security_settings), "__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_security_settings(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_security_settings_flattened_error_async(): + client = SecuritySettingsServiceAsyncClient( + 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_security_settings( + security_settings.DeleteSecuritySettingsRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.SecuritySettingsServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.SecuritySettingsServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = SecuritySettingsServiceClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.SecuritySettingsServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = SecuritySettingsServiceClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.SecuritySettingsServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = SecuritySettingsServiceClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.SecuritySettingsServiceGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.SecuritySettingsServiceGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.SecuritySettingsServiceGrpcTransport, + transports.SecuritySettingsServiceGrpcAsyncIOTransport, + ], +) +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 = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials(), + ) + assert isinstance( + client.transport, transports.SecuritySettingsServiceGrpcTransport, + ) + + +def test_security_settings_service_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.SecuritySettingsServiceTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_security_settings_service_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.security_settings_service.transports.SecuritySettingsServiceTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.SecuritySettingsServiceTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "create_security_settings", + "get_security_settings", + "update_security_settings", + "list_security_settings", + "delete_security_settings", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_security_settings_service_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.dialogflowcx_v3.services.security_settings_service.transports.SecuritySettingsServiceTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.SecuritySettingsServiceTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_security_settings_service_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.dialogflowcx_v3.services.security_settings_service.transports.SecuritySettingsServiceTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.SecuritySettingsServiceTransport() + adc.assert_called_once() + + +def test_security_settings_service_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) + SecuritySettingsServiceClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_security_settings_service_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.SecuritySettingsServiceGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_security_settings_service_host_no_port(): + client = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_security_settings_service_host_with_port(): + client = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_security_settings_service_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.SecuritySettingsServiceGrpcTransport( + 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_security_settings_service_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.SecuritySettingsServiceGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.SecuritySettingsServiceGrpcTransport, + transports.SecuritySettingsServiceGrpcAsyncIOTransport, + ], +) +def test_security_settings_service_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.SecuritySettingsServiceGrpcTransport, + transports.SecuritySettingsServiceGrpcAsyncIOTransport, + ], +) +def test_security_settings_service_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_security_settings_path(): + project = "squid" + location = "clam" + security_settings = "whelk" + + expected = "projects/{project}/locations/{location}/securitySettings/{security_settings}".format( + project=project, location=location, security_settings=security_settings, + ) + actual = SecuritySettingsServiceClient.security_settings_path( + project, location, security_settings + ) + assert expected == actual + + +def test_parse_security_settings_path(): + expected = { + "project": "octopus", + "location": "oyster", + "security_settings": "nudibranch", + } + path = SecuritySettingsServiceClient.security_settings_path(**expected) + + # Check that the path construction is reversible. + actual = SecuritySettingsServiceClient.parse_security_settings_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "cuttlefish" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = SecuritySettingsServiceClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "mussel", + } + path = SecuritySettingsServiceClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = SecuritySettingsServiceClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "winkle" + + expected = "folders/{folder}".format(folder=folder,) + actual = SecuritySettingsServiceClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nautilus", + } + path = SecuritySettingsServiceClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = SecuritySettingsServiceClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "scallop" + + expected = "organizations/{organization}".format(organization=organization,) + actual = SecuritySettingsServiceClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "abalone", + } + path = SecuritySettingsServiceClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = SecuritySettingsServiceClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "squid" + + expected = "projects/{project}".format(project=project,) + actual = SecuritySettingsServiceClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "clam", + } + path = SecuritySettingsServiceClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = SecuritySettingsServiceClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "whelk" + location = "octopus" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = SecuritySettingsServiceClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + } + path = SecuritySettingsServiceClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = SecuritySettingsServiceClient.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.SecuritySettingsServiceTransport, "_prep_wrapped_messages" + ) as prep: + client = SecuritySettingsServiceClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.SecuritySettingsServiceTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = SecuritySettingsServiceClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3/test_session_entity_types.py b/tests/unit/gapic/dialogflowcx_v3/test_session_entity_types.py new file mode 100644 index 00000000..4abcbe55 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_session_entity_types.py @@ -0,0 +1,2264 @@ +# -*- 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.dialogflowcx_v3.services.session_entity_types import ( + SessionEntityTypesAsyncClient, +) +from google.cloud.dialogflowcx_v3.services.session_entity_types import ( + SessionEntityTypesClient, +) +from google.cloud.dialogflowcx_v3.services.session_entity_types import pagers +from google.cloud.dialogflowcx_v3.services.session_entity_types import transports +from google.cloud.dialogflowcx_v3.types import entity_type +from google.cloud.dialogflowcx_v3.types import session_entity_type +from google.cloud.dialogflowcx_v3.types import ( + session_entity_type as gcdc_session_entity_type, +) +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # 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 SessionEntityTypesClient._get_default_mtls_endpoint(None) is None + assert ( + SessionEntityTypesClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + SessionEntityTypesClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + SessionEntityTypesClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + SessionEntityTypesClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + SessionEntityTypesClient._get_default_mtls_endpoint(non_googleapi) + == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [SessionEntityTypesClient, SessionEntityTypesAsyncClient] +) +def test_session_entity_types_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 == "dialogflow.googleapis.com:443" + + +def test_session_entity_types_client_get_transport_class(): + transport = SessionEntityTypesClient.get_transport_class() + assert transport == transports.SessionEntityTypesGrpcTransport + + transport = SessionEntityTypesClient.get_transport_class("grpc") + assert transport == transports.SessionEntityTypesGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (SessionEntityTypesClient, transports.SessionEntityTypesGrpcTransport, "grpc"), + ( + SessionEntityTypesAsyncClient, + transports.SessionEntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + SessionEntityTypesClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SessionEntityTypesClient), +) +@mock.patch.object( + SessionEntityTypesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SessionEntityTypesAsyncClient), +) +def test_session_entity_types_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(SessionEntityTypesClient, "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(SessionEntityTypesClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + ( + SessionEntityTypesClient, + transports.SessionEntityTypesGrpcTransport, + "grpc", + "true", + ), + ( + SessionEntityTypesAsyncClient, + transports.SessionEntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + ( + SessionEntityTypesClient, + transports.SessionEntityTypesGrpcTransport, + "grpc", + "false", + ), + ( + SessionEntityTypesAsyncClient, + transports.SessionEntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + SessionEntityTypesClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SessionEntityTypesClient), +) +@mock.patch.object( + SessionEntityTypesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SessionEntityTypesAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_session_entity_types_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (SessionEntityTypesClient, transports.SessionEntityTypesGrpcTransport, "grpc"), + ( + SessionEntityTypesAsyncClient, + transports.SessionEntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_session_entity_types_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (SessionEntityTypesClient, transports.SessionEntityTypesGrpcTransport, "grpc"), + ( + SessionEntityTypesAsyncClient, + transports.SessionEntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_session_entity_types_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_session_entity_types_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.session_entity_types.transports.SessionEntityTypesGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = SessionEntityTypesClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_session_entity_types( + transport: str = "grpc", + request_type=session_entity_type.ListSessionEntityTypesRequest, +): + client = SessionEntityTypesClient( + 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_session_entity_types), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = session_entity_type.ListSessionEntityTypesResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_session_entity_types(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.ListSessionEntityTypesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListSessionEntityTypesPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_session_entity_types_from_dict(): + test_list_session_entity_types(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_session_entity_types_async( + transport: str = "grpc_asyncio", + request_type=session_entity_type.ListSessionEntityTypesRequest, +): + client = SessionEntityTypesAsyncClient( + 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_session_entity_types), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session_entity_type.ListSessionEntityTypesResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_session_entity_types(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.ListSessionEntityTypesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListSessionEntityTypesAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_session_entity_types_async_from_dict(): + await test_list_session_entity_types_async(request_type=dict) + + +def test_list_session_entity_types_field_headers(): + client = SessionEntityTypesClient(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 = session_entity_type.ListSessionEntityTypesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_session_entity_types), "__call__" + ) as call: + call.return_value = session_entity_type.ListSessionEntityTypesResponse() + + client.list_session_entity_types(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_session_entity_types_field_headers_async(): + client = SessionEntityTypesAsyncClient( + 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 = session_entity_type.ListSessionEntityTypesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_session_entity_types), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session_entity_type.ListSessionEntityTypesResponse() + ) + + await client.list_session_entity_types(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_session_entity_types_flattened(): + client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_session_entity_types), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = session_entity_type.ListSessionEntityTypesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_session_entity_types(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_session_entity_types_flattened_error(): + client = SessionEntityTypesClient(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_session_entity_types( + session_entity_type.ListSessionEntityTypesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_session_entity_types_flattened_async(): + client = SessionEntityTypesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_session_entity_types), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = session_entity_type.ListSessionEntityTypesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session_entity_type.ListSessionEntityTypesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_session_entity_types(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_session_entity_types_flattened_error_async(): + client = SessionEntityTypesAsyncClient( + 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_session_entity_types( + session_entity_type.ListSessionEntityTypesRequest(), parent="parent_value", + ) + + +def test_list_session_entity_types_pager(): + client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_session_entity_types), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[ + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + ], + next_page_token="abc", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[], next_page_token="def", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[session_entity_type.SessionEntityType(),], + next_page_token="ghi", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[ + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + ], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_session_entity_types(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all( + isinstance(i, session_entity_type.SessionEntityType) for i in results + ) + + +def test_list_session_entity_types_pages(): + client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_session_entity_types), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[ + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + ], + next_page_token="abc", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[], next_page_token="def", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[session_entity_type.SessionEntityType(),], + next_page_token="ghi", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[ + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + ], + ), + RuntimeError, + ) + pages = list(client.list_session_entity_types(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_session_entity_types_async_pager(): + client = SessionEntityTypesAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_session_entity_types), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[ + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + ], + next_page_token="abc", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[], next_page_token="def", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[session_entity_type.SessionEntityType(),], + next_page_token="ghi", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[ + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + ], + ), + RuntimeError, + ) + async_pager = await client.list_session_entity_types(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, session_entity_type.SessionEntityType) for i in responses + ) + + +@pytest.mark.asyncio +async def test_list_session_entity_types_async_pages(): + client = SessionEntityTypesAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_session_entity_types), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[ + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + ], + next_page_token="abc", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[], next_page_token="def", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[session_entity_type.SessionEntityType(),], + next_page_token="ghi", + ), + session_entity_type.ListSessionEntityTypesResponse( + session_entity_types=[ + session_entity_type.SessionEntityType(), + session_entity_type.SessionEntityType(), + ], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_session_entity_types(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_session_entity_type( + transport: str = "grpc", + request_type=session_entity_type.GetSessionEntityTypeRequest, +): + client = SessionEntityTypesClient( + 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_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = session_entity_type.SessionEntityType( + name="name_value", + entity_override_mode=session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE, + ) + + response = client.get_session_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.GetSessionEntityTypeRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, session_entity_type.SessionEntityType) + + assert response.name == "name_value" + + assert ( + response.entity_override_mode + == session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE + ) + + +def test_get_session_entity_type_from_dict(): + test_get_session_entity_type(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_session_entity_type_async( + transport: str = "grpc_asyncio", + request_type=session_entity_type.GetSessionEntityTypeRequest, +): + client = SessionEntityTypesAsyncClient( + 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_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session_entity_type.SessionEntityType( + name="name_value", + entity_override_mode=session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE, + ) + ) + + response = await client.get_session_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.GetSessionEntityTypeRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, session_entity_type.SessionEntityType) + + assert response.name == "name_value" + + assert ( + response.entity_override_mode + == session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE + ) + + +@pytest.mark.asyncio +async def test_get_session_entity_type_async_from_dict(): + await test_get_session_entity_type_async(request_type=dict) + + +def test_get_session_entity_type_field_headers(): + client = SessionEntityTypesClient(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 = session_entity_type.GetSessionEntityTypeRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_session_entity_type), "__call__" + ) as call: + call.return_value = session_entity_type.SessionEntityType() + + client.get_session_entity_type(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_session_entity_type_field_headers_async(): + client = SessionEntityTypesAsyncClient( + 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 = session_entity_type.GetSessionEntityTypeRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_session_entity_type), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session_entity_type.SessionEntityType() + ) + + await client.get_session_entity_type(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_session_entity_type_flattened(): + client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = session_entity_type.SessionEntityType() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_session_entity_type(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_session_entity_type_flattened_error(): + client = SessionEntityTypesClient(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_session_entity_type( + session_entity_type.GetSessionEntityTypeRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_session_entity_type_flattened_async(): + client = SessionEntityTypesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = session_entity_type.SessionEntityType() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session_entity_type.SessionEntityType() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_session_entity_type(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_session_entity_type_flattened_error_async(): + client = SessionEntityTypesAsyncClient( + 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_session_entity_type( + session_entity_type.GetSessionEntityTypeRequest(), name="name_value", + ) + + +def test_create_session_entity_type( + transport: str = "grpc", + request_type=gcdc_session_entity_type.CreateSessionEntityTypeRequest, +): + client = SessionEntityTypesClient( + 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_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_session_entity_type.SessionEntityType( + name="name_value", + entity_override_mode=gcdc_session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE, + ) + + response = client.create_session_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_session_entity_type.CreateSessionEntityTypeRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_session_entity_type.SessionEntityType) + + assert response.name == "name_value" + + assert ( + response.entity_override_mode + == gcdc_session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE + ) + + +def test_create_session_entity_type_from_dict(): + test_create_session_entity_type(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_session_entity_type_async( + transport: str = "grpc_asyncio", + request_type=gcdc_session_entity_type.CreateSessionEntityTypeRequest, +): + client = SessionEntityTypesAsyncClient( + 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_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_session_entity_type.SessionEntityType( + name="name_value", + entity_override_mode=gcdc_session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE, + ) + ) + + response = await client.create_session_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_session_entity_type.CreateSessionEntityTypeRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_session_entity_type.SessionEntityType) + + assert response.name == "name_value" + + assert ( + response.entity_override_mode + == gcdc_session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE + ) + + +@pytest.mark.asyncio +async def test_create_session_entity_type_async_from_dict(): + await test_create_session_entity_type_async(request_type=dict) + + +def test_create_session_entity_type_field_headers(): + client = SessionEntityTypesClient(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 = gcdc_session_entity_type.CreateSessionEntityTypeRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_session_entity_type), "__call__" + ) as call: + call.return_value = gcdc_session_entity_type.SessionEntityType() + + client.create_session_entity_type(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_session_entity_type_field_headers_async(): + client = SessionEntityTypesAsyncClient( + 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 = gcdc_session_entity_type.CreateSessionEntityTypeRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_session_entity_type), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_session_entity_type.SessionEntityType() + ) + + await client.create_session_entity_type(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_session_entity_type_flattened(): + client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_session_entity_type.SessionEntityType() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_session_entity_type( + parent="parent_value", + session_entity_type=gcdc_session_entity_type.SessionEntityType( + 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 + ].session_entity_type == gcdc_session_entity_type.SessionEntityType( + name="name_value" + ) + + +def test_create_session_entity_type_flattened_error(): + client = SessionEntityTypesClient(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_session_entity_type( + gcdc_session_entity_type.CreateSessionEntityTypeRequest(), + parent="parent_value", + session_entity_type=gcdc_session_entity_type.SessionEntityType( + name="name_value" + ), + ) + + +@pytest.mark.asyncio +async def test_create_session_entity_type_flattened_async(): + client = SessionEntityTypesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_session_entity_type.SessionEntityType() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_session_entity_type.SessionEntityType() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_session_entity_type( + parent="parent_value", + session_entity_type=gcdc_session_entity_type.SessionEntityType( + 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 + ].session_entity_type == gcdc_session_entity_type.SessionEntityType( + name="name_value" + ) + + +@pytest.mark.asyncio +async def test_create_session_entity_type_flattened_error_async(): + client = SessionEntityTypesAsyncClient( + 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_session_entity_type( + gcdc_session_entity_type.CreateSessionEntityTypeRequest(), + parent="parent_value", + session_entity_type=gcdc_session_entity_type.SessionEntityType( + name="name_value" + ), + ) + + +def test_update_session_entity_type( + transport: str = "grpc", + request_type=gcdc_session_entity_type.UpdateSessionEntityTypeRequest, +): + client = SessionEntityTypesClient( + 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_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_session_entity_type.SessionEntityType( + name="name_value", + entity_override_mode=gcdc_session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE, + ) + + response = client.update_session_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_session_entity_type.UpdateSessionEntityTypeRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_session_entity_type.SessionEntityType) + + assert response.name == "name_value" + + assert ( + response.entity_override_mode + == gcdc_session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE + ) + + +def test_update_session_entity_type_from_dict(): + test_update_session_entity_type(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_session_entity_type_async( + transport: str = "grpc_asyncio", + request_type=gcdc_session_entity_type.UpdateSessionEntityTypeRequest, +): + client = SessionEntityTypesAsyncClient( + 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_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_session_entity_type.SessionEntityType( + name="name_value", + entity_override_mode=gcdc_session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE, + ) + ) + + response = await client.update_session_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_session_entity_type.UpdateSessionEntityTypeRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_session_entity_type.SessionEntityType) + + assert response.name == "name_value" + + assert ( + response.entity_override_mode + == gcdc_session_entity_type.SessionEntityType.EntityOverrideMode.ENTITY_OVERRIDE_MODE_OVERRIDE + ) + + +@pytest.mark.asyncio +async def test_update_session_entity_type_async_from_dict(): + await test_update_session_entity_type_async(request_type=dict) + + +def test_update_session_entity_type_field_headers(): + client = SessionEntityTypesClient(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 = gcdc_session_entity_type.UpdateSessionEntityTypeRequest() + request.session_entity_type.name = "session_entity_type.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_session_entity_type), "__call__" + ) as call: + call.return_value = gcdc_session_entity_type.SessionEntityType() + + client.update_session_entity_type(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", + "session_entity_type.name=session_entity_type.name/value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_session_entity_type_field_headers_async(): + client = SessionEntityTypesAsyncClient( + 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 = gcdc_session_entity_type.UpdateSessionEntityTypeRequest() + request.session_entity_type.name = "session_entity_type.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_session_entity_type), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_session_entity_type.SessionEntityType() + ) + + await client.update_session_entity_type(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", + "session_entity_type.name=session_entity_type.name/value", + ) in kw["metadata"] + + +def test_update_session_entity_type_flattened(): + client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_session_entity_type.SessionEntityType() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_session_entity_type( + session_entity_type=gcdc_session_entity_type.SessionEntityType( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[ + 0 + ].session_entity_type == gcdc_session_entity_type.SessionEntityType( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_session_entity_type_flattened_error(): + client = SessionEntityTypesClient(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_session_entity_type( + gcdc_session_entity_type.UpdateSessionEntityTypeRequest(), + session_entity_type=gcdc_session_entity_type.SessionEntityType( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_session_entity_type_flattened_async(): + client = SessionEntityTypesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_session_entity_type.SessionEntityType() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_session_entity_type.SessionEntityType() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_session_entity_type( + session_entity_type=gcdc_session_entity_type.SessionEntityType( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[ + 0 + ].session_entity_type == gcdc_session_entity_type.SessionEntityType( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_session_entity_type_flattened_error_async(): + client = SessionEntityTypesAsyncClient( + 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_session_entity_type( + gcdc_session_entity_type.UpdateSessionEntityTypeRequest(), + session_entity_type=gcdc_session_entity_type.SessionEntityType( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_session_entity_type( + transport: str = "grpc", + request_type=session_entity_type.DeleteSessionEntityTypeRequest, +): + client = SessionEntityTypesClient( + 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_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_session_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.DeleteSessionEntityTypeRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_session_entity_type_from_dict(): + test_delete_session_entity_type(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_session_entity_type_async( + transport: str = "grpc_asyncio", + request_type=session_entity_type.DeleteSessionEntityTypeRequest, +): + client = SessionEntityTypesAsyncClient( + 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_session_entity_type), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_session_entity_type(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.DeleteSessionEntityTypeRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_session_entity_type_async_from_dict(): + await test_delete_session_entity_type_async(request_type=dict) + + +def test_delete_session_entity_type_field_headers(): + client = SessionEntityTypesClient(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 = session_entity_type.DeleteSessionEntityTypeRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_session_entity_type), "__call__" + ) as call: + call.return_value = None + + client.delete_session_entity_type(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_session_entity_type_field_headers_async(): + client = SessionEntityTypesAsyncClient( + 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 = session_entity_type.DeleteSessionEntityTypeRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_session_entity_type), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_session_entity_type(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_session_entity_type_flattened(): + client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_session_entity_type), "__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_session_entity_type(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_session_entity_type_flattened_error(): + client = SessionEntityTypesClient(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_session_entity_type( + session_entity_type.DeleteSessionEntityTypeRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_session_entity_type_flattened_async(): + client = SessionEntityTypesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_session_entity_type), "__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_session_entity_type(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_session_entity_type_flattened_error_async(): + client = SessionEntityTypesAsyncClient( + 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_session_entity_type( + session_entity_type.DeleteSessionEntityTypeRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.SessionEntityTypesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.SessionEntityTypesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = SessionEntityTypesClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.SessionEntityTypesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = SessionEntityTypesClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.SessionEntityTypesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = SessionEntityTypesClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.SessionEntityTypesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.SessionEntityTypesGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.SessionEntityTypesGrpcTransport, + transports.SessionEntityTypesGrpcAsyncIOTransport, + ], +) +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 = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.SessionEntityTypesGrpcTransport,) + + +def test_session_entity_types_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.SessionEntityTypesTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_session_entity_types_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.session_entity_types.transports.SessionEntityTypesTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.SessionEntityTypesTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_session_entity_types", + "get_session_entity_type", + "create_session_entity_type", + "update_session_entity_type", + "delete_session_entity_type", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_session_entity_types_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.dialogflowcx_v3.services.session_entity_types.transports.SessionEntityTypesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.SessionEntityTypesTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_session_entity_types_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.dialogflowcx_v3.services.session_entity_types.transports.SessionEntityTypesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.SessionEntityTypesTransport() + adc.assert_called_once() + + +def test_session_entity_types_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) + SessionEntityTypesClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_session_entity_types_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.SessionEntityTypesGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_session_entity_types_host_no_port(): + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_session_entity_types_host_with_port(): + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_session_entity_types_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.SessionEntityTypesGrpcTransport( + 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_session_entity_types_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.SessionEntityTypesGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.SessionEntityTypesGrpcTransport, + transports.SessionEntityTypesGrpcAsyncIOTransport, + ], +) +def test_session_entity_types_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.SessionEntityTypesGrpcTransport, + transports.SessionEntityTypesGrpcAsyncIOTransport, + ], +) +def test_session_entity_types_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_session_entity_type_path(): + project = "squid" + location = "clam" + agent = "whelk" + session = "octopus" + entity_type = "oyster" + + expected = "projects/{project}/locations/{location}/agents/{agent}/sessions/{session}/entityTypes/{entity_type}".format( + project=project, + location=location, + agent=agent, + session=session, + entity_type=entity_type, + ) + actual = SessionEntityTypesClient.session_entity_type_path( + project, location, agent, session, entity_type + ) + assert expected == actual + + +def test_parse_session_entity_type_path(): + expected = { + "project": "nudibranch", + "location": "cuttlefish", + "agent": "mussel", + "session": "winkle", + "entity_type": "nautilus", + } + path = SessionEntityTypesClient.session_entity_type_path(**expected) + + # Check that the path construction is reversible. + actual = SessionEntityTypesClient.parse_session_entity_type_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "scallop" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = SessionEntityTypesClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "abalone", + } + path = SessionEntityTypesClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = SessionEntityTypesClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "squid" + + expected = "folders/{folder}".format(folder=folder,) + actual = SessionEntityTypesClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "clam", + } + path = SessionEntityTypesClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = SessionEntityTypesClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "whelk" + + expected = "organizations/{organization}".format(organization=organization,) + actual = SessionEntityTypesClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "octopus", + } + path = SessionEntityTypesClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = SessionEntityTypesClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "oyster" + + expected = "projects/{project}".format(project=project,) + actual = SessionEntityTypesClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nudibranch", + } + path = SessionEntityTypesClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = SessionEntityTypesClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "cuttlefish" + location = "mussel" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = SessionEntityTypesClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "winkle", + "location": "nautilus", + } + path = SessionEntityTypesClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = SessionEntityTypesClient.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.SessionEntityTypesTransport, "_prep_wrapped_messages" + ) as prep: + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.SessionEntityTypesTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = SessionEntityTypesClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3/test_sessions.py b/tests/unit/gapic/dialogflowcx_v3/test_sessions.py new file mode 100644 index 00000000..bfa203f9 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_sessions.py @@ -0,0 +1,1537 @@ +# -*- 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.dialogflowcx_v3.services.sessions import SessionsAsyncClient +from google.cloud.dialogflowcx_v3.services.sessions import SessionsClient +from google.cloud.dialogflowcx_v3.services.sessions import transports +from google.cloud.dialogflowcx_v3.types import audio_config +from google.cloud.dialogflowcx_v3.types import entity_type +from google.cloud.dialogflowcx_v3.types import intent +from google.cloud.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import session +from google.cloud.dialogflowcx_v3.types import session_entity_type +from google.oauth2 import service_account +from google.protobuf import struct_pb2 as struct # type: ignore +from google.type import latlng_pb2 as latlng # 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 SessionsClient._get_default_mtls_endpoint(None) is None + assert SessionsClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + SessionsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + SessionsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + SessionsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert SessionsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [SessionsClient, SessionsAsyncClient]) +def test_sessions_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 == "dialogflow.googleapis.com:443" + + +def test_sessions_client_get_transport_class(): + transport = SessionsClient.get_transport_class() + assert transport == transports.SessionsGrpcTransport + + transport = SessionsClient.get_transport_class("grpc") + assert transport == transports.SessionsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (SessionsClient, transports.SessionsGrpcTransport, "grpc"), + (SessionsAsyncClient, transports.SessionsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +@mock.patch.object( + SessionsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(SessionsClient) +) +@mock.patch.object( + SessionsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SessionsAsyncClient), +) +def test_sessions_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(SessionsClient, "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(SessionsClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + (SessionsClient, transports.SessionsGrpcTransport, "grpc", "true"), + ( + SessionsAsyncClient, + transports.SessionsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (SessionsClient, transports.SessionsGrpcTransport, "grpc", "false"), + ( + SessionsAsyncClient, + transports.SessionsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + SessionsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(SessionsClient) +) +@mock.patch.object( + SessionsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SessionsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_sessions_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (SessionsClient, transports.SessionsGrpcTransport, "grpc"), + (SessionsAsyncClient, transports.SessionsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_sessions_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (SessionsClient, transports.SessionsGrpcTransport, "grpc"), + (SessionsAsyncClient, transports.SessionsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_sessions_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_sessions_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.sessions.transports.SessionsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = SessionsClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_detect_intent( + transport: str = "grpc", request_type=session.DetectIntentRequest +): + client = SessionsClient( + 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.detect_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = session.DetectIntentResponse( + response_id="response_id_value", output_audio=b"output_audio_blob", + ) + + response = client.detect_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == session.DetectIntentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, session.DetectIntentResponse) + + assert response.response_id == "response_id_value" + + assert response.output_audio == b"output_audio_blob" + + +def test_detect_intent_from_dict(): + test_detect_intent(request_type=dict) + + +@pytest.mark.asyncio +async def test_detect_intent_async( + transport: str = "grpc_asyncio", request_type=session.DetectIntentRequest +): + client = SessionsAsyncClient( + 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.detect_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session.DetectIntentResponse( + response_id="response_id_value", output_audio=b"output_audio_blob", + ) + ) + + response = await client.detect_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == session.DetectIntentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, session.DetectIntentResponse) + + assert response.response_id == "response_id_value" + + assert response.output_audio == b"output_audio_blob" + + +@pytest.mark.asyncio +async def test_detect_intent_async_from_dict(): + await test_detect_intent_async(request_type=dict) + + +def test_detect_intent_field_headers(): + client = SessionsClient(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 = session.DetectIntentRequest() + request.session = "session/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.detect_intent), "__call__") as call: + call.return_value = session.DetectIntentResponse() + + client.detect_intent(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", "session=session/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_detect_intent_field_headers_async(): + client = SessionsAsyncClient(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 = session.DetectIntentRequest() + request.session = "session/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.detect_intent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session.DetectIntentResponse() + ) + + await client.detect_intent(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", "session=session/value",) in kw["metadata"] + + +def test_streaming_detect_intent( + transport: str = "grpc", request_type=session.StreamingDetectIntentRequest +): + client = SessionsClient( + 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() + + requests = [request] + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.streaming_detect_intent), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iter([session.StreamingDetectIntentResponse()]) + + response = client.streaming_detect_intent(iter(requests)) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert next(args[0]) == request + + # Establish that the response is the type that we expect. + for message in response: + assert isinstance(message, session.StreamingDetectIntentResponse) + + +def test_streaming_detect_intent_from_dict(): + test_streaming_detect_intent(request_type=dict) + + +@pytest.mark.asyncio +async def test_streaming_detect_intent_async( + transport: str = "grpc_asyncio", request_type=session.StreamingDetectIntentRequest +): + client = SessionsAsyncClient( + 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() + + requests = [request] + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.streaming_detect_intent), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = mock.Mock(aio.StreamStreamCall, autospec=True) + call.return_value.read = mock.AsyncMock( + side_effect=[session.StreamingDetectIntentResponse()] + ) + + response = await client.streaming_detect_intent(iter(requests)) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert next(args[0]) == request + + # Establish that the response is the type that we expect. + message = await response.read() + assert isinstance(message, session.StreamingDetectIntentResponse) + + +@pytest.mark.asyncio +async def test_streaming_detect_intent_async_from_dict(): + await test_streaming_detect_intent_async(request_type=dict) + + +def test_match_intent(transport: str = "grpc", request_type=session.MatchIntentRequest): + client = SessionsClient( + 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.match_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = session.MatchIntentResponse(text="text_value",) + + response = client.match_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == session.MatchIntentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, session.MatchIntentResponse) + + +def test_match_intent_from_dict(): + test_match_intent(request_type=dict) + + +@pytest.mark.asyncio +async def test_match_intent_async( + transport: str = "grpc_asyncio", request_type=session.MatchIntentRequest +): + client = SessionsAsyncClient( + 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.match_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session.MatchIntentResponse() + ) + + response = await client.match_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == session.MatchIntentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, session.MatchIntentResponse) + + +@pytest.mark.asyncio +async def test_match_intent_async_from_dict(): + await test_match_intent_async(request_type=dict) + + +def test_match_intent_field_headers(): + client = SessionsClient(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 = session.MatchIntentRequest() + request.session = "session/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.match_intent), "__call__") as call: + call.return_value = session.MatchIntentResponse() + + client.match_intent(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", "session=session/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_match_intent_field_headers_async(): + client = SessionsAsyncClient(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 = session.MatchIntentRequest() + request.session = "session/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.match_intent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session.MatchIntentResponse() + ) + + await client.match_intent(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", "session=session/value",) in kw["metadata"] + + +def test_fulfill_intent( + transport: str = "grpc", request_type=session.FulfillIntentRequest +): + client = SessionsClient( + 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.fulfill_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = session.FulfillIntentResponse( + response_id="response_id_value", output_audio=b"output_audio_blob", + ) + + response = client.fulfill_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == session.FulfillIntentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, session.FulfillIntentResponse) + + assert response.response_id == "response_id_value" + + assert response.output_audio == b"output_audio_blob" + + +def test_fulfill_intent_from_dict(): + test_fulfill_intent(request_type=dict) + + +@pytest.mark.asyncio +async def test_fulfill_intent_async( + transport: str = "grpc_asyncio", request_type=session.FulfillIntentRequest +): + client = SessionsAsyncClient( + 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.fulfill_intent), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session.FulfillIntentResponse( + response_id="response_id_value", output_audio=b"output_audio_blob", + ) + ) + + response = await client.fulfill_intent(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == session.FulfillIntentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, session.FulfillIntentResponse) + + assert response.response_id == "response_id_value" + + assert response.output_audio == b"output_audio_blob" + + +@pytest.mark.asyncio +async def test_fulfill_intent_async_from_dict(): + await test_fulfill_intent_async(request_type=dict) + + +def test_fulfill_intent_field_headers(): + client = SessionsClient(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 = session.FulfillIntentRequest() + request.match_intent_request.session = "match_intent_request.session/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.fulfill_intent), "__call__") as call: + call.return_value = session.FulfillIntentResponse() + + client.fulfill_intent(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", + "match_intent_request.session=match_intent_request.session/value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_fulfill_intent_field_headers_async(): + client = SessionsAsyncClient(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 = session.FulfillIntentRequest() + request.match_intent_request.session = "match_intent_request.session/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.fulfill_intent), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + session.FulfillIntentResponse() + ) + + await client.fulfill_intent(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", + "match_intent_request.session=match_intent_request.session/value", + ) in kw["metadata"] + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.SessionsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = SessionsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.SessionsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = SessionsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.SessionsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = SessionsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.SessionsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = SessionsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.SessionsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.SessionsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], +) +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 = SessionsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.SessionsGrpcTransport,) + + +def test_sessions_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.SessionsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_sessions_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.sessions.transports.SessionsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.SessionsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "detect_intent", + "streaming_detect_intent", + "match_intent", + "fulfill_intent", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_sessions_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.dialogflowcx_v3.services.sessions.transports.SessionsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.SessionsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_sessions_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.dialogflowcx_v3.services.sessions.transports.SessionsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.SessionsTransport() + adc.assert_called_once() + + +def test_sessions_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) + SessionsClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_sessions_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.SessionsGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_sessions_host_no_port(): + client = SessionsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_sessions_host_with_port(): + client = SessionsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_sessions_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.SessionsGrpcTransport( + 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_sessions_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.SessionsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], +) +def test_sessions_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], +) +def test_sessions_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_entity_type_path(): + project = "squid" + location = "clam" + agent = "whelk" + entity_type = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + actual = SessionsClient.entity_type_path(project, location, agent, entity_type) + assert expected == actual + + +def test_parse_entity_type_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "entity_type": "mussel", + } + path = SessionsClient.entity_type_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_entity_type_path(path) + assert expected == actual + + +def test_flow_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + flow = "abalone" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + actual = SessionsClient.flow_path(project, location, agent, flow) + assert expected == actual + + +def test_parse_flow_path(): + expected = { + "project": "squid", + "location": "clam", + "agent": "whelk", + "flow": "octopus", + } + path = SessionsClient.flow_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_flow_path(path) + assert expected == actual + + +def test_intent_path(): + project = "oyster" + location = "nudibranch" + agent = "cuttlefish" + intent = "mussel" + + expected = "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + actual = SessionsClient.intent_path(project, location, agent, intent) + assert expected == actual + + +def test_parse_intent_path(): + expected = { + "project": "winkle", + "location": "nautilus", + "agent": "scallop", + "intent": "abalone", + } + path = SessionsClient.intent_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_intent_path(path) + assert expected == actual + + +def test_page_path(): + project = "squid" + location = "clam" + agent = "whelk" + flow = "octopus" + page = "oyster" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + actual = SessionsClient.page_path(project, location, agent, flow, page) + assert expected == actual + + +def test_parse_page_path(): + expected = { + "project": "nudibranch", + "location": "cuttlefish", + "agent": "mussel", + "flow": "winkle", + "page": "nautilus", + } + path = SessionsClient.page_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_page_path(path) + assert expected == actual + + +def test_session_path(): + project = "scallop" + location = "abalone" + agent = "squid" + session = "clam" + + expected = "projects/{project}/locations/{location}/agents/{agent}/sessions/{session}".format( + project=project, location=location, agent=agent, session=session, + ) + actual = SessionsClient.session_path(project, location, agent, session) + assert expected == actual + + +def test_parse_session_path(): + expected = { + "project": "whelk", + "location": "octopus", + "agent": "oyster", + "session": "nudibranch", + } + path = SessionsClient.session_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_session_path(path) + assert expected == actual + + +def test_session_entity_type_path(): + project = "cuttlefish" + location = "mussel" + agent = "winkle" + session = "nautilus" + entity_type = "scallop" + + expected = "projects/{project}/locations/{location}/agents/{agent}/sessions/{session}/entityTypes/{entity_type}".format( + project=project, + location=location, + agent=agent, + session=session, + entity_type=entity_type, + ) + actual = SessionsClient.session_entity_type_path( + project, location, agent, session, entity_type + ) + assert expected == actual + + +def test_parse_session_entity_type_path(): + expected = { + "project": "abalone", + "location": "squid", + "agent": "clam", + "session": "whelk", + "entity_type": "octopus", + } + path = SessionsClient.session_entity_type_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_session_entity_type_path(path) + assert expected == actual + + +def test_transition_route_group_path(): + project = "oyster" + location = "nudibranch" + agent = "cuttlefish" + flow = "mussel" + transition_route_group = "winkle" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/transitionRouteGroups/{transition_route_group}".format( + project=project, + location=location, + agent=agent, + flow=flow, + transition_route_group=transition_route_group, + ) + actual = SessionsClient.transition_route_group_path( + project, location, agent, flow, transition_route_group + ) + assert expected == actual + + +def test_parse_transition_route_group_path(): + expected = { + "project": "nautilus", + "location": "scallop", + "agent": "abalone", + "flow": "squid", + "transition_route_group": "clam", + } + path = SessionsClient.transition_route_group_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_transition_route_group_path(path) + assert expected == actual + + +def test_webhook_path(): + project = "whelk" + location = "octopus" + agent = "oyster" + webhook = "nudibranch" + + expected = "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + actual = SessionsClient.webhook_path(project, location, agent, webhook) + assert expected == actual + + +def test_parse_webhook_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + "agent": "winkle", + "webhook": "nautilus", + } + path = SessionsClient.webhook_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_webhook_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "scallop" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = SessionsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "abalone", + } + path = SessionsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "squid" + + expected = "folders/{folder}".format(folder=folder,) + actual = SessionsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "clam", + } + path = SessionsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "whelk" + + expected = "organizations/{organization}".format(organization=organization,) + actual = SessionsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "octopus", + } + path = SessionsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "oyster" + + expected = "projects/{project}".format(project=project,) + actual = SessionsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nudibranch", + } + path = SessionsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "cuttlefish" + location = "mussel" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = SessionsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "winkle", + "location": "nautilus", + } + path = SessionsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.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.SessionsTransport, "_prep_wrapped_messages" + ) as prep: + client = SessionsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.SessionsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = SessionsClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3/test_transition_route_groups.py b/tests/unit/gapic/dialogflowcx_v3/test_transition_route_groups.py new file mode 100644 index 00000000..efcf25c4 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_transition_route_groups.py @@ -0,0 +1,2420 @@ +# -*- 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.dialogflowcx_v3.services.transition_route_groups import ( + TransitionRouteGroupsAsyncClient, +) +from google.cloud.dialogflowcx_v3.services.transition_route_groups import ( + TransitionRouteGroupsClient, +) +from google.cloud.dialogflowcx_v3.services.transition_route_groups import pagers +from google.cloud.dialogflowcx_v3.services.transition_route_groups import transports +from google.cloud.dialogflowcx_v3.types import fulfillment +from google.cloud.dialogflowcx_v3.types import page +from google.cloud.dialogflowcx_v3.types import response_message +from google.cloud.dialogflowcx_v3.types import transition_route_group +from google.cloud.dialogflowcx_v3.types import ( + transition_route_group as gcdc_transition_route_group, +) +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # 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 TransitionRouteGroupsClient._get_default_mtls_endpoint(None) is None + assert ( + TransitionRouteGroupsClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + TransitionRouteGroupsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + TransitionRouteGroupsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + TransitionRouteGroupsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + TransitionRouteGroupsClient._get_default_mtls_endpoint(non_googleapi) + == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [TransitionRouteGroupsClient, TransitionRouteGroupsAsyncClient] +) +def test_transition_route_groups_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 == "dialogflow.googleapis.com:443" + + +def test_transition_route_groups_client_get_transport_class(): + transport = TransitionRouteGroupsClient.get_transport_class() + assert transport == transports.TransitionRouteGroupsGrpcTransport + + transport = TransitionRouteGroupsClient.get_transport_class("grpc") + assert transport == transports.TransitionRouteGroupsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + TransitionRouteGroupsClient, + transports.TransitionRouteGroupsGrpcTransport, + "grpc", + ), + ( + TransitionRouteGroupsAsyncClient, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + TransitionRouteGroupsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TransitionRouteGroupsClient), +) +@mock.patch.object( + TransitionRouteGroupsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TransitionRouteGroupsAsyncClient), +) +def test_transition_route_groups_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(TransitionRouteGroupsClient, "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(TransitionRouteGroupsClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + ( + TransitionRouteGroupsClient, + transports.TransitionRouteGroupsGrpcTransport, + "grpc", + "true", + ), + ( + TransitionRouteGroupsAsyncClient, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + ( + TransitionRouteGroupsClient, + transports.TransitionRouteGroupsGrpcTransport, + "grpc", + "false", + ), + ( + TransitionRouteGroupsAsyncClient, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + TransitionRouteGroupsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TransitionRouteGroupsClient), +) +@mock.patch.object( + TransitionRouteGroupsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TransitionRouteGroupsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_transition_route_groups_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + TransitionRouteGroupsClient, + transports.TransitionRouteGroupsGrpcTransport, + "grpc", + ), + ( + TransitionRouteGroupsAsyncClient, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_transition_route_groups_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + TransitionRouteGroupsClient, + transports.TransitionRouteGroupsGrpcTransport, + "grpc", + ), + ( + TransitionRouteGroupsAsyncClient, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_transition_route_groups_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_transition_route_groups_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.transition_route_groups.transports.TransitionRouteGroupsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = TransitionRouteGroupsClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_transition_route_groups( + transport: str = "grpc", + request_type=transition_route_group.ListTransitionRouteGroupsRequest, +): + client = TransitionRouteGroupsClient( + 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_transition_route_groups), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = transition_route_group.ListTransitionRouteGroupsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_transition_route_groups(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == transition_route_group.ListTransitionRouteGroupsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListTransitionRouteGroupsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_transition_route_groups_from_dict(): + test_list_transition_route_groups(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_transition_route_groups_async( + transport: str = "grpc_asyncio", + request_type=transition_route_group.ListTransitionRouteGroupsRequest, +): + client = TransitionRouteGroupsAsyncClient( + 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_transition_route_groups), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + transition_route_group.ListTransitionRouteGroupsResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_transition_route_groups(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == transition_route_group.ListTransitionRouteGroupsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTransitionRouteGroupsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_transition_route_groups_async_from_dict(): + await test_list_transition_route_groups_async(request_type=dict) + + +def test_list_transition_route_groups_field_headers(): + client = TransitionRouteGroupsClient( + 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 = transition_route_group.ListTransitionRouteGroupsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_transition_route_groups), "__call__" + ) as call: + call.return_value = transition_route_group.ListTransitionRouteGroupsResponse() + + client.list_transition_route_groups(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_transition_route_groups_field_headers_async(): + client = TransitionRouteGroupsAsyncClient( + 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 = transition_route_group.ListTransitionRouteGroupsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_transition_route_groups), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + transition_route_group.ListTransitionRouteGroupsResponse() + ) + + await client.list_transition_route_groups(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_transition_route_groups_flattened(): + client = TransitionRouteGroupsClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_transition_route_groups), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = transition_route_group.ListTransitionRouteGroupsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_transition_route_groups(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_transition_route_groups_flattened_error(): + client = TransitionRouteGroupsClient( + 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_transition_route_groups( + transition_route_group.ListTransitionRouteGroupsRequest(), + parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_transition_route_groups_flattened_async(): + client = TransitionRouteGroupsAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_transition_route_groups), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = transition_route_group.ListTransitionRouteGroupsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + transition_route_group.ListTransitionRouteGroupsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_transition_route_groups(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_transition_route_groups_flattened_error_async(): + client = TransitionRouteGroupsAsyncClient( + 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_transition_route_groups( + transition_route_group.ListTransitionRouteGroupsRequest(), + parent="parent_value", + ) + + +def test_list_transition_route_groups_pager(): + client = TransitionRouteGroupsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_transition_route_groups), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + ], + next_page_token="abc", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[], next_page_token="def", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + ], + next_page_token="ghi", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + ], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_transition_route_groups(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all( + isinstance(i, transition_route_group.TransitionRouteGroup) for i in results + ) + + +def test_list_transition_route_groups_pages(): + client = TransitionRouteGroupsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_transition_route_groups), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + ], + next_page_token="abc", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[], next_page_token="def", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + ], + next_page_token="ghi", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + ], + ), + RuntimeError, + ) + pages = list(client.list_transition_route_groups(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_transition_route_groups_async_pager(): + client = TransitionRouteGroupsAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_transition_route_groups), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + ], + next_page_token="abc", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[], next_page_token="def", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + ], + next_page_token="ghi", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + ], + ), + RuntimeError, + ) + async_pager = await client.list_transition_route_groups(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, transition_route_group.TransitionRouteGroup) + for i in responses + ) + + +@pytest.mark.asyncio +async def test_list_transition_route_groups_async_pages(): + client = TransitionRouteGroupsAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_transition_route_groups), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + ], + next_page_token="abc", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[], next_page_token="def", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + ], + next_page_token="ghi", + ), + transition_route_group.ListTransitionRouteGroupsResponse( + transition_route_groups=[ + transition_route_group.TransitionRouteGroup(), + transition_route_group.TransitionRouteGroup(), + ], + ), + RuntimeError, + ) + pages = [] + async for page_ in ( + await client.list_transition_route_groups(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_transition_route_group( + transport: str = "grpc", + request_type=transition_route_group.GetTransitionRouteGroupRequest, +): + client = TransitionRouteGroupsClient( + 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_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = transition_route_group.TransitionRouteGroup( + name="name_value", display_name="display_name_value", + ) + + response = client.get_transition_route_group(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == transition_route_group.GetTransitionRouteGroupRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, transition_route_group.TransitionRouteGroup) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + +def test_get_transition_route_group_from_dict(): + test_get_transition_route_group(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_transition_route_group_async( + transport: str = "grpc_asyncio", + request_type=transition_route_group.GetTransitionRouteGroupRequest, +): + client = TransitionRouteGroupsAsyncClient( + 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_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + transition_route_group.TransitionRouteGroup( + name="name_value", display_name="display_name_value", + ) + ) + + response = await client.get_transition_route_group(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == transition_route_group.GetTransitionRouteGroupRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, transition_route_group.TransitionRouteGroup) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + +@pytest.mark.asyncio +async def test_get_transition_route_group_async_from_dict(): + await test_get_transition_route_group_async(request_type=dict) + + +def test_get_transition_route_group_field_headers(): + client = TransitionRouteGroupsClient( + 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 = transition_route_group.GetTransitionRouteGroupRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_transition_route_group), "__call__" + ) as call: + call.return_value = transition_route_group.TransitionRouteGroup() + + client.get_transition_route_group(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_transition_route_group_field_headers_async(): + client = TransitionRouteGroupsAsyncClient( + 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 = transition_route_group.GetTransitionRouteGroupRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_transition_route_group), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + transition_route_group.TransitionRouteGroup() + ) + + await client.get_transition_route_group(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_transition_route_group_flattened(): + client = TransitionRouteGroupsClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = transition_route_group.TransitionRouteGroup() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_transition_route_group(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_transition_route_group_flattened_error(): + client = TransitionRouteGroupsClient( + 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_transition_route_group( + transition_route_group.GetTransitionRouteGroupRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_transition_route_group_flattened_async(): + client = TransitionRouteGroupsAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = transition_route_group.TransitionRouteGroup() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + transition_route_group.TransitionRouteGroup() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_transition_route_group(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_transition_route_group_flattened_error_async(): + client = TransitionRouteGroupsAsyncClient( + 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_transition_route_group( + transition_route_group.GetTransitionRouteGroupRequest(), name="name_value", + ) + + +def test_create_transition_route_group( + transport: str = "grpc", + request_type=gcdc_transition_route_group.CreateTransitionRouteGroupRequest, +): + client = TransitionRouteGroupsClient( + 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_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_transition_route_group.TransitionRouteGroup( + name="name_value", display_name="display_name_value", + ) + + response = client.create_transition_route_group(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert ( + args[0] == gcdc_transition_route_group.CreateTransitionRouteGroupRequest() + ) + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_transition_route_group.TransitionRouteGroup) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + +def test_create_transition_route_group_from_dict(): + test_create_transition_route_group(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_transition_route_group_async( + transport: str = "grpc_asyncio", + request_type=gcdc_transition_route_group.CreateTransitionRouteGroupRequest, +): + client = TransitionRouteGroupsAsyncClient( + 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_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_transition_route_group.TransitionRouteGroup( + name="name_value", display_name="display_name_value", + ) + ) + + response = await client.create_transition_route_group(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert ( + args[0] == gcdc_transition_route_group.CreateTransitionRouteGroupRequest() + ) + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_transition_route_group.TransitionRouteGroup) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + +@pytest.mark.asyncio +async def test_create_transition_route_group_async_from_dict(): + await test_create_transition_route_group_async(request_type=dict) + + +def test_create_transition_route_group_field_headers(): + client = TransitionRouteGroupsClient( + 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 = gcdc_transition_route_group.CreateTransitionRouteGroupRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_transition_route_group), "__call__" + ) as call: + call.return_value = gcdc_transition_route_group.TransitionRouteGroup() + + client.create_transition_route_group(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_transition_route_group_field_headers_async(): + client = TransitionRouteGroupsAsyncClient( + 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 = gcdc_transition_route_group.CreateTransitionRouteGroupRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_transition_route_group), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_transition_route_group.TransitionRouteGroup() + ) + + await client.create_transition_route_group(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_transition_route_group_flattened(): + client = TransitionRouteGroupsClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_transition_route_group.TransitionRouteGroup() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_transition_route_group( + parent="parent_value", + transition_route_group=gcdc_transition_route_group.TransitionRouteGroup( + 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 + ].transition_route_group == gcdc_transition_route_group.TransitionRouteGroup( + name="name_value" + ) + + +def test_create_transition_route_group_flattened_error(): + client = TransitionRouteGroupsClient( + 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_transition_route_group( + gcdc_transition_route_group.CreateTransitionRouteGroupRequest(), + parent="parent_value", + transition_route_group=gcdc_transition_route_group.TransitionRouteGroup( + name="name_value" + ), + ) + + +@pytest.mark.asyncio +async def test_create_transition_route_group_flattened_async(): + client = TransitionRouteGroupsAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_transition_route_group.TransitionRouteGroup() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_transition_route_group.TransitionRouteGroup() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_transition_route_group( + parent="parent_value", + transition_route_group=gcdc_transition_route_group.TransitionRouteGroup( + 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 + ].transition_route_group == gcdc_transition_route_group.TransitionRouteGroup( + name="name_value" + ) + + +@pytest.mark.asyncio +async def test_create_transition_route_group_flattened_error_async(): + client = TransitionRouteGroupsAsyncClient( + 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_transition_route_group( + gcdc_transition_route_group.CreateTransitionRouteGroupRequest(), + parent="parent_value", + transition_route_group=gcdc_transition_route_group.TransitionRouteGroup( + name="name_value" + ), + ) + + +def test_update_transition_route_group( + transport: str = "grpc", + request_type=gcdc_transition_route_group.UpdateTransitionRouteGroupRequest, +): + client = TransitionRouteGroupsClient( + 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_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_transition_route_group.TransitionRouteGroup( + name="name_value", display_name="display_name_value", + ) + + response = client.update_transition_route_group(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert ( + args[0] == gcdc_transition_route_group.UpdateTransitionRouteGroupRequest() + ) + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_transition_route_group.TransitionRouteGroup) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + +def test_update_transition_route_group_from_dict(): + test_update_transition_route_group(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_transition_route_group_async( + transport: str = "grpc_asyncio", + request_type=gcdc_transition_route_group.UpdateTransitionRouteGroupRequest, +): + client = TransitionRouteGroupsAsyncClient( + 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_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_transition_route_group.TransitionRouteGroup( + name="name_value", display_name="display_name_value", + ) + ) + + response = await client.update_transition_route_group(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert ( + args[0] == gcdc_transition_route_group.UpdateTransitionRouteGroupRequest() + ) + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_transition_route_group.TransitionRouteGroup) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + +@pytest.mark.asyncio +async def test_update_transition_route_group_async_from_dict(): + await test_update_transition_route_group_async(request_type=dict) + + +def test_update_transition_route_group_field_headers(): + client = TransitionRouteGroupsClient( + 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 = gcdc_transition_route_group.UpdateTransitionRouteGroupRequest() + request.transition_route_group.name = "transition_route_group.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_transition_route_group), "__call__" + ) as call: + call.return_value = gcdc_transition_route_group.TransitionRouteGroup() + + client.update_transition_route_group(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", + "transition_route_group.name=transition_route_group.name/value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_transition_route_group_field_headers_async(): + client = TransitionRouteGroupsAsyncClient( + 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 = gcdc_transition_route_group.UpdateTransitionRouteGroupRequest() + request.transition_route_group.name = "transition_route_group.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_transition_route_group), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_transition_route_group.TransitionRouteGroup() + ) + + await client.update_transition_route_group(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", + "transition_route_group.name=transition_route_group.name/value", + ) in kw["metadata"] + + +def test_update_transition_route_group_flattened(): + client = TransitionRouteGroupsClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_transition_route_group.TransitionRouteGroup() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_transition_route_group( + transition_route_group=gcdc_transition_route_group.TransitionRouteGroup( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[ + 0 + ].transition_route_group == gcdc_transition_route_group.TransitionRouteGroup( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_transition_route_group_flattened_error(): + client = TransitionRouteGroupsClient( + 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_transition_route_group( + gcdc_transition_route_group.UpdateTransitionRouteGroupRequest(), + transition_route_group=gcdc_transition_route_group.TransitionRouteGroup( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_transition_route_group_flattened_async(): + client = TransitionRouteGroupsAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_transition_route_group.TransitionRouteGroup() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_transition_route_group.TransitionRouteGroup() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_transition_route_group( + transition_route_group=gcdc_transition_route_group.TransitionRouteGroup( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[ + 0 + ].transition_route_group == gcdc_transition_route_group.TransitionRouteGroup( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_transition_route_group_flattened_error_async(): + client = TransitionRouteGroupsAsyncClient( + 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_transition_route_group( + gcdc_transition_route_group.UpdateTransitionRouteGroupRequest(), + transition_route_group=gcdc_transition_route_group.TransitionRouteGroup( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_transition_route_group( + transport: str = "grpc", + request_type=transition_route_group.DeleteTransitionRouteGroupRequest, +): + client = TransitionRouteGroupsClient( + 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_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_transition_route_group(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == transition_route_group.DeleteTransitionRouteGroupRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_transition_route_group_from_dict(): + test_delete_transition_route_group(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_transition_route_group_async( + transport: str = "grpc_asyncio", + request_type=transition_route_group.DeleteTransitionRouteGroupRequest, +): + client = TransitionRouteGroupsAsyncClient( + 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_transition_route_group), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_transition_route_group(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == transition_route_group.DeleteTransitionRouteGroupRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_transition_route_group_async_from_dict(): + await test_delete_transition_route_group_async(request_type=dict) + + +def test_delete_transition_route_group_field_headers(): + client = TransitionRouteGroupsClient( + 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 = transition_route_group.DeleteTransitionRouteGroupRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_transition_route_group), "__call__" + ) as call: + call.return_value = None + + client.delete_transition_route_group(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_transition_route_group_field_headers_async(): + client = TransitionRouteGroupsAsyncClient( + 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 = transition_route_group.DeleteTransitionRouteGroupRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_transition_route_group), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_transition_route_group(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_transition_route_group_flattened(): + client = TransitionRouteGroupsClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_transition_route_group), "__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_transition_route_group(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_transition_route_group_flattened_error(): + client = TransitionRouteGroupsClient( + 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_transition_route_group( + transition_route_group.DeleteTransitionRouteGroupRequest(), + name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_transition_route_group_flattened_async(): + client = TransitionRouteGroupsAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_transition_route_group), "__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_transition_route_group(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_transition_route_group_flattened_error_async(): + client = TransitionRouteGroupsAsyncClient( + 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_transition_route_group( + transition_route_group.DeleteTransitionRouteGroupRequest(), + name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.TransitionRouteGroupsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TransitionRouteGroupsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.TransitionRouteGroupsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TransitionRouteGroupsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.TransitionRouteGroupsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = TransitionRouteGroupsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.TransitionRouteGroupsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = TransitionRouteGroupsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.TransitionRouteGroupsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.TransitionRouteGroupsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.TransitionRouteGroupsGrpcTransport, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + ], +) +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 = TransitionRouteGroupsClient( + credentials=credentials.AnonymousCredentials(), + ) + assert isinstance(client.transport, transports.TransitionRouteGroupsGrpcTransport,) + + +def test_transition_route_groups_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.TransitionRouteGroupsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_transition_route_groups_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.transition_route_groups.transports.TransitionRouteGroupsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.TransitionRouteGroupsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_transition_route_groups", + "get_transition_route_group", + "create_transition_route_group", + "update_transition_route_group", + "delete_transition_route_group", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_transition_route_groups_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.dialogflowcx_v3.services.transition_route_groups.transports.TransitionRouteGroupsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.TransitionRouteGroupsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_transition_route_groups_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.dialogflowcx_v3.services.transition_route_groups.transports.TransitionRouteGroupsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.TransitionRouteGroupsTransport() + adc.assert_called_once() + + +def test_transition_route_groups_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) + TransitionRouteGroupsClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_transition_route_groups_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.TransitionRouteGroupsGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_transition_route_groups_host_no_port(): + client = TransitionRouteGroupsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_transition_route_groups_host_with_port(): + client = TransitionRouteGroupsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_transition_route_groups_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.TransitionRouteGroupsGrpcTransport( + 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_transition_route_groups_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.TransitionRouteGroupsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.TransitionRouteGroupsGrpcTransport, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + ], +) +def test_transition_route_groups_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.TransitionRouteGroupsGrpcTransport, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + ], +) +def test_transition_route_groups_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_flow_path(): + project = "squid" + location = "clam" + agent = "whelk" + flow = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + actual = TransitionRouteGroupsClient.flow_path(project, location, agent, flow) + assert expected == actual + + +def test_parse_flow_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "flow": "mussel", + } + path = TransitionRouteGroupsClient.flow_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_flow_path(path) + assert expected == actual + + +def test_intent_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + intent = "abalone" + + expected = "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + actual = TransitionRouteGroupsClient.intent_path(project, location, agent, intent) + assert expected == actual + + +def test_parse_intent_path(): + expected = { + "project": "squid", + "location": "clam", + "agent": "whelk", + "intent": "octopus", + } + path = TransitionRouteGroupsClient.intent_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_intent_path(path) + assert expected == actual + + +def test_page_path(): + project = "oyster" + location = "nudibranch" + agent = "cuttlefish" + flow = "mussel" + page = "winkle" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + actual = TransitionRouteGroupsClient.page_path(project, location, agent, flow, page) + assert expected == actual + + +def test_parse_page_path(): + expected = { + "project": "nautilus", + "location": "scallop", + "agent": "abalone", + "flow": "squid", + "page": "clam", + } + path = TransitionRouteGroupsClient.page_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_page_path(path) + assert expected == actual + + +def test_transition_route_group_path(): + project = "whelk" + location = "octopus" + agent = "oyster" + flow = "nudibranch" + transition_route_group = "cuttlefish" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/transitionRouteGroups/{transition_route_group}".format( + project=project, + location=location, + agent=agent, + flow=flow, + transition_route_group=transition_route_group, + ) + actual = TransitionRouteGroupsClient.transition_route_group_path( + project, location, agent, flow, transition_route_group + ) + assert expected == actual + + +def test_parse_transition_route_group_path(): + expected = { + "project": "mussel", + "location": "winkle", + "agent": "nautilus", + "flow": "scallop", + "transition_route_group": "abalone", + } + path = TransitionRouteGroupsClient.transition_route_group_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_transition_route_group_path(path) + assert expected == actual + + +def test_webhook_path(): + project = "squid" + location = "clam" + agent = "whelk" + webhook = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + actual = TransitionRouteGroupsClient.webhook_path(project, location, agent, webhook) + assert expected == actual + + +def test_parse_webhook_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "webhook": "mussel", + } + path = TransitionRouteGroupsClient.webhook_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_webhook_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "winkle" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = TransitionRouteGroupsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nautilus", + } + path = TransitionRouteGroupsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "scallop" + + expected = "folders/{folder}".format(folder=folder,) + actual = TransitionRouteGroupsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "abalone", + } + path = TransitionRouteGroupsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "squid" + + expected = "organizations/{organization}".format(organization=organization,) + actual = TransitionRouteGroupsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "clam", + } + path = TransitionRouteGroupsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "whelk" + + expected = "projects/{project}".format(project=project,) + actual = TransitionRouteGroupsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "octopus", + } + path = TransitionRouteGroupsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.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 = TransitionRouteGroupsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + } + path = TransitionRouteGroupsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.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.TransitionRouteGroupsTransport, "_prep_wrapped_messages" + ) as prep: + client = TransitionRouteGroupsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.TransitionRouteGroupsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = TransitionRouteGroupsClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3/test_versions.py b/tests/unit/gapic/dialogflowcx_v3/test_versions.py new file mode 100644 index 00000000..8693eca7 --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_versions.py @@ -0,0 +1,2216 @@ +# -*- 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 future +from google.api_core import gapic_v1 +from google.api_core import grpc_helpers +from google.api_core import grpc_helpers_async +from google.api_core import operation_async # type: ignore +from google.api_core import operations_v1 +from google.auth import credentials +from google.auth.exceptions import MutualTLSChannelError +from google.cloud.dialogflowcx_v3.services.versions import VersionsAsyncClient +from google.cloud.dialogflowcx_v3.services.versions import VersionsClient +from google.cloud.dialogflowcx_v3.services.versions import pagers +from google.cloud.dialogflowcx_v3.services.versions import transports +from google.cloud.dialogflowcx_v3.types import flow +from google.cloud.dialogflowcx_v3.types import version +from google.cloud.dialogflowcx_v3.types import version as gcdc_version +from google.longrunning import operations_pb2 +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import struct_pb2 as struct # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # 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 VersionsClient._get_default_mtls_endpoint(None) is None + assert VersionsClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + VersionsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + VersionsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + VersionsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert VersionsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [VersionsClient, VersionsAsyncClient]) +def test_versions_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 == "dialogflow.googleapis.com:443" + + +def test_versions_client_get_transport_class(): + transport = VersionsClient.get_transport_class() + assert transport == transports.VersionsGrpcTransport + + transport = VersionsClient.get_transport_class("grpc") + assert transport == transports.VersionsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (VersionsClient, transports.VersionsGrpcTransport, "grpc"), + (VersionsAsyncClient, transports.VersionsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +@mock.patch.object( + VersionsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(VersionsClient) +) +@mock.patch.object( + VersionsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(VersionsAsyncClient), +) +def test_versions_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(VersionsClient, "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(VersionsClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + (VersionsClient, transports.VersionsGrpcTransport, "grpc", "true"), + ( + VersionsAsyncClient, + transports.VersionsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (VersionsClient, transports.VersionsGrpcTransport, "grpc", "false"), + ( + VersionsAsyncClient, + transports.VersionsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + VersionsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(VersionsClient) +) +@mock.patch.object( + VersionsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(VersionsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_versions_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (VersionsClient, transports.VersionsGrpcTransport, "grpc"), + (VersionsAsyncClient, transports.VersionsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_versions_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (VersionsClient, transports.VersionsGrpcTransport, "grpc"), + (VersionsAsyncClient, transports.VersionsGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_versions_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_versions_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.versions.transports.VersionsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = VersionsClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_versions( + transport: str = "grpc", request_type=version.ListVersionsRequest +): + client = VersionsClient( + 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_versions), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = version.ListVersionsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_versions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == version.ListVersionsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListVersionsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_versions_from_dict(): + test_list_versions(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_versions_async( + transport: str = "grpc_asyncio", request_type=version.ListVersionsRequest +): + client = VersionsAsyncClient( + 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_versions), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + version.ListVersionsResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_versions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == version.ListVersionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListVersionsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_versions_async_from_dict(): + await test_list_versions_async(request_type=dict) + + +def test_list_versions_field_headers(): + client = VersionsClient(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 = version.ListVersionsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: + call.return_value = version.ListVersionsResponse() + + client.list_versions(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_versions_field_headers_async(): + client = VersionsAsyncClient(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 = version.ListVersionsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + version.ListVersionsResponse() + ) + + await client.list_versions(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_versions_flattened(): + client = VersionsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = version.ListVersionsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_versions(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_versions_flattened_error(): + client = VersionsClient(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_versions( + version.ListVersionsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_versions_flattened_async(): + client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = version.ListVersionsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + version.ListVersionsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_versions(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_versions_flattened_error_async(): + client = VersionsAsyncClient(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_versions( + version.ListVersionsRequest(), parent="parent_value", + ) + + +def test_list_versions_pager(): + client = VersionsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + version.ListVersionsResponse( + versions=[version.Version(), version.Version(), version.Version(),], + next_page_token="abc", + ), + version.ListVersionsResponse(versions=[], next_page_token="def",), + version.ListVersionsResponse( + versions=[version.Version(),], next_page_token="ghi", + ), + version.ListVersionsResponse( + versions=[version.Version(), version.Version(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_versions(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, version.Version) for i in results) + + +def test_list_versions_pages(): + client = VersionsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + version.ListVersionsResponse( + versions=[version.Version(), version.Version(), version.Version(),], + next_page_token="abc", + ), + version.ListVersionsResponse(versions=[], next_page_token="def",), + version.ListVersionsResponse( + versions=[version.Version(),], next_page_token="ghi", + ), + version.ListVersionsResponse( + versions=[version.Version(), version.Version(),], + ), + RuntimeError, + ) + pages = list(client.list_versions(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_versions_async_pager(): + client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_versions), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + version.ListVersionsResponse( + versions=[version.Version(), version.Version(), version.Version(),], + next_page_token="abc", + ), + version.ListVersionsResponse(versions=[], next_page_token="def",), + version.ListVersionsResponse( + versions=[version.Version(),], next_page_token="ghi", + ), + version.ListVersionsResponse( + versions=[version.Version(), version.Version(),], + ), + RuntimeError, + ) + async_pager = await client.list_versions(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, version.Version) for i in responses) + + +@pytest.mark.asyncio +async def test_list_versions_async_pages(): + client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_versions), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + version.ListVersionsResponse( + versions=[version.Version(), version.Version(), version.Version(),], + next_page_token="abc", + ), + version.ListVersionsResponse(versions=[], next_page_token="def",), + version.ListVersionsResponse( + versions=[version.Version(),], next_page_token="ghi", + ), + version.ListVersionsResponse( + versions=[version.Version(), version.Version(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_versions(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_version(transport: str = "grpc", request_type=version.GetVersionRequest): + client = VersionsClient( + 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_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = version.Version( + name="name_value", + display_name="display_name_value", + description="description_value", + state=version.Version.State.RUNNING, + ) + + response = client.get_version(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == version.GetVersionRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, version.Version) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + assert response.state == version.Version.State.RUNNING + + +def test_get_version_from_dict(): + test_get_version(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_version_async( + transport: str = "grpc_asyncio", request_type=version.GetVersionRequest +): + client = VersionsAsyncClient( + 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_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + version.Version( + name="name_value", + display_name="display_name_value", + description="description_value", + state=version.Version.State.RUNNING, + ) + ) + + response = await client.get_version(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == version.GetVersionRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, version.Version) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + assert response.state == version.Version.State.RUNNING + + +@pytest.mark.asyncio +async def test_get_version_async_from_dict(): + await test_get_version_async(request_type=dict) + + +def test_get_version_field_headers(): + client = VersionsClient(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 = version.GetVersionRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_version), "__call__") as call: + call.return_value = version.Version() + + client.get_version(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_version_field_headers_async(): + client = VersionsAsyncClient(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 = version.GetVersionRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_version), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(version.Version()) + + await client.get_version(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_version_flattened(): + client = VersionsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = version.Version() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_version(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_version_flattened_error(): + client = VersionsClient(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_version( + version.GetVersionRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_version_flattened_async(): + client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = version.Version() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(version.Version()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_version(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_version_flattened_error_async(): + client = VersionsAsyncClient(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_version( + version.GetVersionRequest(), name="name_value", + ) + + +def test_create_version( + transport: str = "grpc", request_type=gcdc_version.CreateVersionRequest +): + client = VersionsClient( + 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_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.create_version(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_version.CreateVersionRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_create_version_from_dict(): + test_create_version(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_version_async( + transport: str = "grpc_asyncio", request_type=gcdc_version.CreateVersionRequest +): + client = VersionsAsyncClient( + 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_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + + response = await client.create_version(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_version.CreateVersionRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_create_version_async_from_dict(): + await test_create_version_async(request_type=dict) + + +def test_create_version_field_headers(): + client = VersionsClient(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 = gcdc_version.CreateVersionRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_version), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.create_version(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_version_field_headers_async(): + client = VersionsAsyncClient(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 = gcdc_version.CreateVersionRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_version), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.create_version(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_version_flattened(): + client = VersionsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_version( + parent="parent_value", version=gcdc_version.Version(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].version == gcdc_version.Version(name="name_value") + + +def test_create_version_flattened_error(): + client = VersionsClient(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_version( + gcdc_version.CreateVersionRequest(), + parent="parent_value", + version=gcdc_version.Version(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_version_flattened_async(): + client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_version( + parent="parent_value", version=gcdc_version.Version(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].version == gcdc_version.Version(name="name_value") + + +@pytest.mark.asyncio +async def test_create_version_flattened_error_async(): + client = VersionsAsyncClient(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_version( + gcdc_version.CreateVersionRequest(), + parent="parent_value", + version=gcdc_version.Version(name="name_value"), + ) + + +def test_update_version( + transport: str = "grpc", request_type=gcdc_version.UpdateVersionRequest +): + client = VersionsClient( + 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_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_version.Version( + name="name_value", + display_name="display_name_value", + description="description_value", + state=gcdc_version.Version.State.RUNNING, + ) + + response = client.update_version(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_version.UpdateVersionRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_version.Version) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + assert response.state == gcdc_version.Version.State.RUNNING + + +def test_update_version_from_dict(): + test_update_version(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_version_async( + transport: str = "grpc_asyncio", request_type=gcdc_version.UpdateVersionRequest +): + client = VersionsAsyncClient( + 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_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_version.Version( + name="name_value", + display_name="display_name_value", + description="description_value", + state=gcdc_version.Version.State.RUNNING, + ) + ) + + response = await client.update_version(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_version.UpdateVersionRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_version.Version) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.description == "description_value" + + assert response.state == gcdc_version.Version.State.RUNNING + + +@pytest.mark.asyncio +async def test_update_version_async_from_dict(): + await test_update_version_async(request_type=dict) + + +def test_update_version_field_headers(): + client = VersionsClient(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 = gcdc_version.UpdateVersionRequest() + request.version.name = "version.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_version), "__call__") as call: + call.return_value = gcdc_version.Version() + + client.update_version(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", "version.name=version.name/value",) in kw[ + "metadata" + ] + + +@pytest.mark.asyncio +async def test_update_version_field_headers_async(): + client = VersionsAsyncClient(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 = gcdc_version.UpdateVersionRequest() + request.version.name = "version.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_version), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_version.Version() + ) + + await client.update_version(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", "version.name=version.name/value",) in kw[ + "metadata" + ] + + +def test_update_version_flattened(): + client = VersionsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_version.Version() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_version( + version=gcdc_version.Version(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].version == gcdc_version.Version(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_version_flattened_error(): + client = VersionsClient(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_version( + gcdc_version.UpdateVersionRequest(), + version=gcdc_version.Version(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_version_flattened_async(): + client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_version.Version() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_version.Version() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_version( + version=gcdc_version.Version(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].version == gcdc_version.Version(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_version_flattened_error_async(): + client = VersionsAsyncClient(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_version( + gcdc_version.UpdateVersionRequest(), + version=gcdc_version.Version(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_version( + transport: str = "grpc", request_type=version.DeleteVersionRequest +): + client = VersionsClient( + 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_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_version(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == version.DeleteVersionRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_version_from_dict(): + test_delete_version(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_version_async( + transport: str = "grpc_asyncio", request_type=version.DeleteVersionRequest +): + client = VersionsAsyncClient( + 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_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_version(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == version.DeleteVersionRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_version_async_from_dict(): + await test_delete_version_async(request_type=dict) + + +def test_delete_version_field_headers(): + client = VersionsClient(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 = version.DeleteVersionRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_version), "__call__") as call: + call.return_value = None + + client.delete_version(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_version_field_headers_async(): + client = VersionsAsyncClient(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 = version.DeleteVersionRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_version), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_version(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_version_flattened(): + client = VersionsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_version), "__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_version(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_version_flattened_error(): + client = VersionsClient(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_version( + version.DeleteVersionRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_version_flattened_async(): + client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_version), "__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_version(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_version_flattened_error_async(): + client = VersionsAsyncClient(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_version( + version.DeleteVersionRequest(), name="name_value", + ) + + +def test_load_version(transport: str = "grpc", request_type=version.LoadVersionRequest): + client = VersionsClient( + 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.load_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.load_version(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == version.LoadVersionRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_load_version_from_dict(): + test_load_version(request_type=dict) + + +@pytest.mark.asyncio +async def test_load_version_async( + transport: str = "grpc_asyncio", request_type=version.LoadVersionRequest +): + client = VersionsAsyncClient( + 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.load_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + + response = await client.load_version(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == version.LoadVersionRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_load_version_async_from_dict(): + await test_load_version_async(request_type=dict) + + +def test_load_version_field_headers(): + client = VersionsClient(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 = version.LoadVersionRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.load_version), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.load_version(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_load_version_field_headers_async(): + client = VersionsAsyncClient(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 = version.LoadVersionRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.load_version), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.load_version(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_load_version_flattened(): + client = VersionsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.load_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.load_version(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_load_version_flattened_error(): + client = VersionsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.load_version( + version.LoadVersionRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_load_version_flattened_async(): + client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.load_version), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.load_version(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_load_version_flattened_error_async(): + client = VersionsAsyncClient(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.load_version( + version.LoadVersionRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.VersionsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = VersionsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.VersionsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = VersionsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.VersionsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = VersionsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.VersionsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = VersionsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.VersionsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.VersionsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.VersionsGrpcTransport, transports.VersionsGrpcAsyncIOTransport], +) +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 = VersionsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.VersionsGrpcTransport,) + + +def test_versions_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.VersionsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_versions_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.versions.transports.VersionsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.VersionsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_versions", + "get_version", + "create_version", + "update_version", + "delete_version", + "load_version", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + # Additionally, the LRO client (a property) should + # also raise NotImplementedError + with pytest.raises(NotImplementedError): + transport.operations_client + + +def test_versions_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.dialogflowcx_v3.services.versions.transports.VersionsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.VersionsTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_versions_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.dialogflowcx_v3.services.versions.transports.VersionsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.VersionsTransport() + adc.assert_called_once() + + +def test_versions_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) + VersionsClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_versions_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.VersionsGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_versions_host_no_port(): + client = VersionsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_versions_host_with_port(): + client = VersionsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_versions_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.VersionsGrpcTransport( + 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_versions_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.VersionsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [transports.VersionsGrpcTransport, transports.VersionsGrpcAsyncIOTransport], +) +def test_versions_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [transports.VersionsGrpcTransport, transports.VersionsGrpcAsyncIOTransport], +) +def test_versions_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_versions_grpc_lro_client(): + client = VersionsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_versions_grpc_lro_async_client(): + client = VersionsAsyncClient( + credentials=credentials.AnonymousCredentials(), transport="grpc_asyncio", + ) + transport = client.transport + + # Ensure that we have a api-core operations client. + assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) + + # Ensure that subsequent calls to the property send the exact same object. + assert transport.operations_client is transport.operations_client + + +def test_version_path(): + project = "squid" + location = "clam" + agent = "whelk" + flow = "octopus" + version = "oyster" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/versions/{version}".format( + project=project, location=location, agent=agent, flow=flow, version=version, + ) + actual = VersionsClient.version_path(project, location, agent, flow, version) + assert expected == actual + + +def test_parse_version_path(): + expected = { + "project": "nudibranch", + "location": "cuttlefish", + "agent": "mussel", + "flow": "winkle", + "version": "nautilus", + } + path = VersionsClient.version_path(**expected) + + # Check that the path construction is reversible. + actual = VersionsClient.parse_version_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "scallop" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = VersionsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "abalone", + } + path = VersionsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = VersionsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "squid" + + expected = "folders/{folder}".format(folder=folder,) + actual = VersionsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "clam", + } + path = VersionsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = VersionsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "whelk" + + expected = "organizations/{organization}".format(organization=organization,) + actual = VersionsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "octopus", + } + path = VersionsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = VersionsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "oyster" + + expected = "projects/{project}".format(project=project,) + actual = VersionsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nudibranch", + } + path = VersionsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = VersionsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "cuttlefish" + location = "mussel" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = VersionsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "winkle", + "location": "nautilus", + } + path = VersionsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = VersionsClient.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.VersionsTransport, "_prep_wrapped_messages" + ) as prep: + client = VersionsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.VersionsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = VersionsClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3/test_webhooks.py b/tests/unit/gapic/dialogflowcx_v3/test_webhooks.py new file mode 100644 index 00000000..ef861daf --- /dev/null +++ b/tests/unit/gapic/dialogflowcx_v3/test_webhooks.py @@ -0,0 +1,1998 @@ +# -*- 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.dialogflowcx_v3.services.webhooks import WebhooksAsyncClient +from google.cloud.dialogflowcx_v3.services.webhooks import WebhooksClient +from google.cloud.dialogflowcx_v3.services.webhooks import pagers +from google.cloud.dialogflowcx_v3.services.webhooks import transports +from google.cloud.dialogflowcx_v3.types import webhook +from google.cloud.dialogflowcx_v3.types import webhook as gcdc_webhook +from google.oauth2 import service_account +from google.protobuf import duration_pb2 as duration # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # 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 WebhooksClient._get_default_mtls_endpoint(None) is None + assert WebhooksClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + WebhooksClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + WebhooksClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + WebhooksClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert WebhooksClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [WebhooksClient, WebhooksAsyncClient]) +def test_webhooks_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 == "dialogflow.googleapis.com:443" + + +def test_webhooks_client_get_transport_class(): + transport = WebhooksClient.get_transport_class() + assert transport == transports.WebhooksGrpcTransport + + transport = WebhooksClient.get_transport_class("grpc") + assert transport == transports.WebhooksGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (WebhooksClient, transports.WebhooksGrpcTransport, "grpc"), + (WebhooksAsyncClient, transports.WebhooksGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +@mock.patch.object( + WebhooksClient, "DEFAULT_ENDPOINT", modify_default_endpoint(WebhooksClient) +) +@mock.patch.object( + WebhooksAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(WebhooksAsyncClient), +) +def test_webhooks_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(WebhooksClient, "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(WebhooksClient, "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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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, + ssl_channel_credentials=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", + [ + (WebhooksClient, transports.WebhooksGrpcTransport, "grpc", "true"), + ( + WebhooksAsyncClient, + transports.WebhooksGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (WebhooksClient, transports.WebhooksGrpcTransport, "grpc", "false"), + ( + WebhooksAsyncClient, + transports.WebhooksGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + WebhooksClient, "DEFAULT_ENDPOINT", modify_default_endpoint(WebhooksClient) +) +@mock.patch.object( + WebhooksAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(WebhooksAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_webhooks_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: + ssl_channel_creds = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds + ): + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # 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.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (WebhooksClient, transports.WebhooksGrpcTransport, "grpc"), + (WebhooksAsyncClient, transports.WebhooksGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_webhooks_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"], + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (WebhooksClient, transports.WebhooksGrpcTransport, "grpc"), + (WebhooksAsyncClient, transports.WebhooksGrpcAsyncIOTransport, "grpc_asyncio"), + ], +) +def test_webhooks_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, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_webhooks_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflowcx_v3.services.webhooks.transports.WebhooksGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = WebhooksClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_webhooks( + transport: str = "grpc", request_type=webhook.ListWebhooksRequest +): + client = WebhooksClient( + 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_webhooks), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = webhook.ListWebhooksResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_webhooks(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == webhook.ListWebhooksRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListWebhooksPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_webhooks_from_dict(): + test_list_webhooks(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_webhooks_async( + transport: str = "grpc_asyncio", request_type=webhook.ListWebhooksRequest +): + client = WebhooksAsyncClient( + 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_webhooks), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + webhook.ListWebhooksResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_webhooks(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == webhook.ListWebhooksRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListWebhooksAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_webhooks_async_from_dict(): + await test_list_webhooks_async(request_type=dict) + + +def test_list_webhooks_field_headers(): + client = WebhooksClient(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 = webhook.ListWebhooksRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: + call.return_value = webhook.ListWebhooksResponse() + + client.list_webhooks(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_webhooks_field_headers_async(): + client = WebhooksAsyncClient(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 = webhook.ListWebhooksRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + webhook.ListWebhooksResponse() + ) + + await client.list_webhooks(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_webhooks_flattened(): + client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = webhook.ListWebhooksResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_webhooks(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_webhooks_flattened_error(): + client = WebhooksClient(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_webhooks( + webhook.ListWebhooksRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_webhooks_flattened_async(): + client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = webhook.ListWebhooksResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + webhook.ListWebhooksResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_webhooks(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_webhooks_flattened_error_async(): + client = WebhooksAsyncClient(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_webhooks( + webhook.ListWebhooksRequest(), parent="parent_value", + ) + + +def test_list_webhooks_pager(): + client = WebhooksClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(), webhook.Webhook(), webhook.Webhook(),], + next_page_token="abc", + ), + webhook.ListWebhooksResponse(webhooks=[], next_page_token="def",), + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(),], next_page_token="ghi", + ), + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(), webhook.Webhook(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_webhooks(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, webhook.Webhook) for i in results) + + +def test_list_webhooks_pages(): + client = WebhooksClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(), webhook.Webhook(), webhook.Webhook(),], + next_page_token="abc", + ), + webhook.ListWebhooksResponse(webhooks=[], next_page_token="def",), + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(),], next_page_token="ghi", + ), + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(), webhook.Webhook(),], + ), + RuntimeError, + ) + pages = list(client.list_webhooks(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_webhooks_async_pager(): + client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_webhooks), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(), webhook.Webhook(), webhook.Webhook(),], + next_page_token="abc", + ), + webhook.ListWebhooksResponse(webhooks=[], next_page_token="def",), + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(),], next_page_token="ghi", + ), + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(), webhook.Webhook(),], + ), + RuntimeError, + ) + async_pager = await client.list_webhooks(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, webhook.Webhook) for i in responses) + + +@pytest.mark.asyncio +async def test_list_webhooks_async_pages(): + client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_webhooks), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(), webhook.Webhook(), webhook.Webhook(),], + next_page_token="abc", + ), + webhook.ListWebhooksResponse(webhooks=[], next_page_token="def",), + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(),], next_page_token="ghi", + ), + webhook.ListWebhooksResponse( + webhooks=[webhook.Webhook(), webhook.Webhook(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_webhooks(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_webhook(transport: str = "grpc", request_type=webhook.GetWebhookRequest): + client = WebhooksClient( + 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_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = webhook.Webhook( + name="name_value", + display_name="display_name_value", + disabled=True, + generic_web_service=webhook.Webhook.GenericWebService(uri="uri_value"), + ) + + response = client.get_webhook(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == webhook.GetWebhookRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, webhook.Webhook) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.disabled is True + + +def test_get_webhook_from_dict(): + test_get_webhook(request_type=dict) + + +@pytest.mark.asyncio +async def test_get_webhook_async( + transport: str = "grpc_asyncio", request_type=webhook.GetWebhookRequest +): + client = WebhooksAsyncClient( + 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_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + webhook.Webhook( + name="name_value", display_name="display_name_value", disabled=True, + ) + ) + + response = await client.get_webhook(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == webhook.GetWebhookRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, webhook.Webhook) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.disabled is True + + +@pytest.mark.asyncio +async def test_get_webhook_async_from_dict(): + await test_get_webhook_async(request_type=dict) + + +def test_get_webhook_field_headers(): + client = WebhooksClient(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 = webhook.GetWebhookRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_webhook), "__call__") as call: + call.return_value = webhook.Webhook() + + client.get_webhook(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_webhook_field_headers_async(): + client = WebhooksAsyncClient(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 = webhook.GetWebhookRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_webhook), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(webhook.Webhook()) + + await client.get_webhook(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_webhook_flattened(): + client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = webhook.Webhook() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_webhook(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_webhook_flattened_error(): + client = WebhooksClient(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_webhook( + webhook.GetWebhookRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_webhook_flattened_async(): + client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = webhook.Webhook() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(webhook.Webhook()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_webhook(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_webhook_flattened_error_async(): + client = WebhooksAsyncClient(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_webhook( + webhook.GetWebhookRequest(), name="name_value", + ) + + +def test_create_webhook( + transport: str = "grpc", request_type=gcdc_webhook.CreateWebhookRequest +): + client = WebhooksClient( + 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_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_webhook.Webhook( + name="name_value", + display_name="display_name_value", + disabled=True, + generic_web_service=gcdc_webhook.Webhook.GenericWebService(uri="uri_value"), + ) + + response = client.create_webhook(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_webhook.CreateWebhookRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_webhook.Webhook) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.disabled is True + + +def test_create_webhook_from_dict(): + test_create_webhook(request_type=dict) + + +@pytest.mark.asyncio +async def test_create_webhook_async( + transport: str = "grpc_asyncio", request_type=gcdc_webhook.CreateWebhookRequest +): + client = WebhooksAsyncClient( + 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_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_webhook.Webhook( + name="name_value", display_name="display_name_value", disabled=True, + ) + ) + + response = await client.create_webhook(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_webhook.CreateWebhookRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_webhook.Webhook) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.disabled is True + + +@pytest.mark.asyncio +async def test_create_webhook_async_from_dict(): + await test_create_webhook_async(request_type=dict) + + +def test_create_webhook_field_headers(): + client = WebhooksClient(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 = gcdc_webhook.CreateWebhookRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_webhook), "__call__") as call: + call.return_value = gcdc_webhook.Webhook() + + client.create_webhook(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_webhook_field_headers_async(): + client = WebhooksAsyncClient(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 = gcdc_webhook.CreateWebhookRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_webhook), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_webhook.Webhook() + ) + + await client.create_webhook(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_webhook_flattened(): + client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_webhook.Webhook() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_webhook( + parent="parent_value", webhook=gcdc_webhook.Webhook(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].webhook == gcdc_webhook.Webhook(name="name_value") + + +def test_create_webhook_flattened_error(): + client = WebhooksClient(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_webhook( + gcdc_webhook.CreateWebhookRequest(), + parent="parent_value", + webhook=gcdc_webhook.Webhook(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_webhook_flattened_async(): + client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_webhook.Webhook() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_webhook.Webhook() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_webhook( + parent="parent_value", webhook=gcdc_webhook.Webhook(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].webhook == gcdc_webhook.Webhook(name="name_value") + + +@pytest.mark.asyncio +async def test_create_webhook_flattened_error_async(): + client = WebhooksAsyncClient(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_webhook( + gcdc_webhook.CreateWebhookRequest(), + parent="parent_value", + webhook=gcdc_webhook.Webhook(name="name_value"), + ) + + +def test_update_webhook( + transport: str = "grpc", request_type=gcdc_webhook.UpdateWebhookRequest +): + client = WebhooksClient( + 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_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_webhook.Webhook( + name="name_value", + display_name="display_name_value", + disabled=True, + generic_web_service=gcdc_webhook.Webhook.GenericWebService(uri="uri_value"), + ) + + response = client.update_webhook(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_webhook.UpdateWebhookRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcdc_webhook.Webhook) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.disabled is True + + +def test_update_webhook_from_dict(): + test_update_webhook(request_type=dict) + + +@pytest.mark.asyncio +async def test_update_webhook_async( + transport: str = "grpc_asyncio", request_type=gcdc_webhook.UpdateWebhookRequest +): + client = WebhooksAsyncClient( + 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_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_webhook.Webhook( + name="name_value", display_name="display_name_value", disabled=True, + ) + ) + + response = await client.update_webhook(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcdc_webhook.UpdateWebhookRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_webhook.Webhook) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.disabled is True + + +@pytest.mark.asyncio +async def test_update_webhook_async_from_dict(): + await test_update_webhook_async(request_type=dict) + + +def test_update_webhook_field_headers(): + client = WebhooksClient(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 = gcdc_webhook.UpdateWebhookRequest() + request.webhook.name = "webhook.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_webhook), "__call__") as call: + call.return_value = gcdc_webhook.Webhook() + + client.update_webhook(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", "webhook.name=webhook.name/value",) in kw[ + "metadata" + ] + + +@pytest.mark.asyncio +async def test_update_webhook_field_headers_async(): + client = WebhooksAsyncClient(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 = gcdc_webhook.UpdateWebhookRequest() + request.webhook.name = "webhook.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_webhook), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_webhook.Webhook() + ) + + await client.update_webhook(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", "webhook.name=webhook.name/value",) in kw[ + "metadata" + ] + + +def test_update_webhook_flattened(): + client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_webhook.Webhook() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_webhook( + webhook=gcdc_webhook.Webhook(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].webhook == gcdc_webhook.Webhook(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_webhook_flattened_error(): + client = WebhooksClient(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_webhook( + gcdc_webhook.UpdateWebhookRequest(), + webhook=gcdc_webhook.Webhook(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_webhook_flattened_async(): + client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcdc_webhook.Webhook() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcdc_webhook.Webhook() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_webhook( + webhook=gcdc_webhook.Webhook(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].webhook == gcdc_webhook.Webhook(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_webhook_flattened_error_async(): + client = WebhooksAsyncClient(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_webhook( + gcdc_webhook.UpdateWebhookRequest(), + webhook=gcdc_webhook.Webhook(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_webhook( + transport: str = "grpc", request_type=webhook.DeleteWebhookRequest +): + client = WebhooksClient( + 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_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_webhook(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == webhook.DeleteWebhookRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_webhook_from_dict(): + test_delete_webhook(request_type=dict) + + +@pytest.mark.asyncio +async def test_delete_webhook_async( + transport: str = "grpc_asyncio", request_type=webhook.DeleteWebhookRequest +): + client = WebhooksAsyncClient( + 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_webhook), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_webhook(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == webhook.DeleteWebhookRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_webhook_async_from_dict(): + await test_delete_webhook_async(request_type=dict) + + +def test_delete_webhook_field_headers(): + client = WebhooksClient(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 = webhook.DeleteWebhookRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_webhook), "__call__") as call: + call.return_value = None + + client.delete_webhook(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_webhook_field_headers_async(): + client = WebhooksAsyncClient(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 = webhook.DeleteWebhookRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_webhook), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_webhook(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_webhook_flattened(): + client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_webhook), "__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_webhook(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_webhook_flattened_error(): + client = WebhooksClient(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_webhook( + webhook.DeleteWebhookRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_webhook_flattened_async(): + client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_webhook), "__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_webhook(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_webhook_flattened_error_async(): + client = WebhooksAsyncClient(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_webhook( + webhook.DeleteWebhookRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.WebhooksGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = WebhooksClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.WebhooksGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = WebhooksClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.WebhooksGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = WebhooksClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.WebhooksGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = WebhooksClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.WebhooksGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.WebhooksGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.WebhooksGrpcTransport, transports.WebhooksGrpcAsyncIOTransport], +) +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 = WebhooksClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.WebhooksGrpcTransport,) + + +def test_webhooks_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.WebhooksTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_webhooks_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflowcx_v3.services.webhooks.transports.WebhooksTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.WebhooksTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_webhooks", + "get_webhook", + "create_webhook", + "update_webhook", + "delete_webhook", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_webhooks_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.dialogflowcx_v3.services.webhooks.transports.WebhooksTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.WebhooksTransport( + credentials_file="credentials.json", quota_project_id="octopus", + ) + load_creds.assert_called_once_with( + "credentials.json", + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_webhooks_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.dialogflowcx_v3.services.webhooks.transports.WebhooksTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.WebhooksTransport() + adc.assert_called_once() + + +def test_webhooks_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) + WebhooksClient() + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id=None, + ) + + +def test_webhooks_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.WebhooksGrpcTransport( + host="squid.clam.whelk", quota_project_id="octopus" + ) + adc.assert_called_once_with( + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + quota_project_id="octopus", + ) + + +def test_webhooks_host_no_port(): + client = WebhooksClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_webhooks_host_with_port(): + client = WebhooksClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_webhooks_grpc_transport_channel(): + channel = grpc.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.WebhooksGrpcTransport( + 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_webhooks_grpc_asyncio_transport_channel(): + channel = aio.insecure_channel("http://localhost/") + + # Check that channel is used if provided. + transport = transports.WebhooksGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +@pytest.mark.parametrize( + "transport_class", + [transports.WebhooksGrpcTransport, transports.WebhooksGrpcAsyncIOTransport], +) +def test_webhooks_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +@pytest.mark.parametrize( + "transport_class", + [transports.WebhooksGrpcTransport, transports.WebhooksGrpcAsyncIOTransport], +) +def test_webhooks_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_webhook_path(): + project = "squid" + location = "clam" + agent = "whelk" + webhook = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + actual = WebhooksClient.webhook_path(project, location, agent, webhook) + assert expected == actual + + +def test_parse_webhook_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "webhook": "mussel", + } + path = WebhooksClient.webhook_path(**expected) + + # Check that the path construction is reversible. + actual = WebhooksClient.parse_webhook_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "winkle" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = WebhooksClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nautilus", + } + path = WebhooksClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = WebhooksClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "scallop" + + expected = "folders/{folder}".format(folder=folder,) + actual = WebhooksClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "abalone", + } + path = WebhooksClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = WebhooksClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "squid" + + expected = "organizations/{organization}".format(organization=organization,) + actual = WebhooksClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "clam", + } + path = WebhooksClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = WebhooksClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "whelk" + + expected = "projects/{project}".format(project=project,) + actual = WebhooksClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "octopus", + } + path = WebhooksClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = WebhooksClient.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 = WebhooksClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + } + path = WebhooksClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = WebhooksClient.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.WebhooksTransport, "_prep_wrapped_messages" + ) as prep: + client = WebhooksClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.WebhooksTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = WebhooksClient.get_transport_class() + transport = transport_class( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) diff --git a/tests/unit/gapic/dialogflowcx_v3beta1/test_agents.py b/tests/unit/gapic/dialogflowcx_v3beta1/test_agents.py index 0237664a..b0973ffb 100644 --- a/tests/unit/gapic/dialogflowcx_v3beta1/test_agents.py +++ b/tests/unit/gapic/dialogflowcx_v3beta1/test_agents.py @@ -31,7 +31,7 @@ from google.api_core import gapic_v1 from google.api_core import grpc_helpers from google.api_core import grpc_helpers_async -from google.api_core import operation_async +from google.api_core import operation_async # type: ignore from google.api_core import operations_v1 from google.auth import credentials from google.auth.exceptions import MutualTLSChannelError @@ -93,12 +93,12 @@ def test_agents_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_agents_client_get_transport_class(): @@ -144,15 +144,14 @@ def test_agents_client_client_options(client_class, transport_class, transport_n credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -161,15 +160,14 @@ def test_agents_client_client_options(client_class, transport_class, transport_n credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -178,95 +176,171 @@ def test_agents_client_client_options(client_class, transport_class, transport_n credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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, + ssl_channel_credentials=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", + [ + (AgentsClient, transports.AgentsGrpcTransport, "grpc", "true"), + ( + AgentsAsyncClient, + transports.AgentsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (AgentsClient, transports.AgentsGrpcTransport, "grpc", "false"), + ( + AgentsAsyncClient, + transports.AgentsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + AgentsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(AgentsClient) +) +@mock.patch.object( + AgentsAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(AgentsAsyncClient) +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_agents_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) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): - with mock.patch.object(transport_class, "__init__") as patched: + ssl_channel_creds = mock.Mock() with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds ): patched.return_value = None - client = client_class() + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=expected_ssl_channel_creds, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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=False, + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=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 has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - 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, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) @pytest.mark.parametrize( @@ -289,8 +363,7 @@ def test_agents_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -316,8 +389,7 @@ def test_agents_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -334,8 +406,7 @@ def test_agents_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -351,7 +422,7 @@ def test_list_agents(transport: str = "grpc", request_type=agent.ListAgentsReque request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_agents), "__call__") as call: + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = agent.ListAgentsResponse( next_page_token="next_page_token_value", @@ -366,6 +437,7 @@ def test_list_agents(transport: str = "grpc", request_type=agent.ListAgentsReque assert args[0] == agent.ListAgentsRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListAgentsPager) assert response.next_page_token == "next_page_token_value" @@ -376,19 +448,19 @@ def test_list_agents_from_dict(): @pytest.mark.asyncio -async def test_list_agents_async(transport: str = "grpc_asyncio"): +async def test_list_agents_async( + transport: str = "grpc_asyncio", request_type=agent.ListAgentsRequest +): client = AgentsAsyncClient( 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 = agent.ListAgentsRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_agents), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( agent.ListAgentsResponse(next_page_token="next_page_token_value",) @@ -400,7 +472,7 @@ async def test_list_agents_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == agent.ListAgentsRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListAgentsAsyncPager) @@ -408,6 +480,11 @@ async def test_list_agents_async(transport: str = "grpc_asyncio"): assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_list_agents_async_from_dict(): + await test_list_agents_async(request_type=dict) + + def test_list_agents_field_headers(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) @@ -417,7 +494,7 @@ def test_list_agents_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_agents), "__call__") as call: + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: call.return_value = agent.ListAgentsResponse() client.list_agents(request) @@ -442,9 +519,7 @@ async def test_list_agents_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_agents), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( agent.ListAgentsResponse() ) @@ -465,7 +540,7 @@ def test_list_agents_flattened(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_agents), "__call__") as call: + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = agent.ListAgentsResponse() @@ -497,9 +572,7 @@ async def test_list_agents_flattened_async(): client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_agents), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = agent.ListAgentsResponse() @@ -534,7 +607,7 @@ def test_list_agents_pager(): client = AgentsClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_agents), "__call__") as call: + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( agent.ListAgentsResponse( @@ -564,7 +637,7 @@ def test_list_agents_pages(): client = AgentsClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_agents), "__call__") as call: + with mock.patch.object(type(client.transport.list_agents), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( agent.ListAgentsResponse( @@ -587,9 +660,7 @@ async def test_list_agents_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_agents), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_agents), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -618,9 +689,7 @@ async def test_list_agents_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_agents), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_agents), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -650,7 +719,7 @@ def test_get_agent(transport: str = "grpc", request_type=agent.GetAgentRequest): request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_agent), "__call__") as call: + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = agent.Agent( name="name_value", @@ -673,6 +742,7 @@ def test_get_agent(transport: str = "grpc", request_type=agent.GetAgentRequest): assert args[0] == agent.GetAgentRequest() # Establish that the response is the type that we expect. + assert isinstance(response, agent.Agent) assert response.name == "name_value" @@ -699,19 +769,19 @@ def test_get_agent_from_dict(): @pytest.mark.asyncio -async def test_get_agent_async(transport: str = "grpc_asyncio"): +async def test_get_agent_async( + transport: str = "grpc_asyncio", request_type=agent.GetAgentRequest +): client = AgentsAsyncClient( 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 = agent.GetAgentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( agent.Agent( @@ -733,7 +803,7 @@ async def test_get_agent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == agent.GetAgentRequest() # Establish that the response is the type that we expect. assert isinstance(response, agent.Agent) @@ -757,6 +827,11 @@ async def test_get_agent_async(transport: str = "grpc_asyncio"): assert response.enable_spell_correction is True +@pytest.mark.asyncio +async def test_get_agent_async_from_dict(): + await test_get_agent_async(request_type=dict) + + def test_get_agent_field_headers(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) @@ -766,7 +841,7 @@ def test_get_agent_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_agent), "__call__") as call: + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: call.return_value = agent.Agent() client.get_agent(request) @@ -791,9 +866,7 @@ async def test_get_agent_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(agent.Agent()) await client.get_agent(request) @@ -812,7 +885,7 @@ def test_get_agent_flattened(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_agent), "__call__") as call: + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = agent.Agent() @@ -844,9 +917,7 @@ async def test_get_agent_flattened_async(): client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = agent.Agent() @@ -887,7 +958,7 @@ def test_create_agent( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_agent), "__call__") as call: + with mock.patch.object(type(client.transport.create_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_agent.Agent( name="name_value", @@ -910,6 +981,7 @@ def test_create_agent( assert args[0] == gcdc_agent.CreateAgentRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_agent.Agent) assert response.name == "name_value" @@ -936,19 +1008,19 @@ def test_create_agent_from_dict(): @pytest.mark.asyncio -async def test_create_agent_async(transport: str = "grpc_asyncio"): +async def test_create_agent_async( + transport: str = "grpc_asyncio", request_type=gcdc_agent.CreateAgentRequest +): client = AgentsAsyncClient( 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 = gcdc_agent.CreateAgentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_agent.Agent( @@ -970,7 +1042,7 @@ async def test_create_agent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_agent.CreateAgentRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_agent.Agent) @@ -994,6 +1066,11 @@ async def test_create_agent_async(transport: str = "grpc_asyncio"): assert response.enable_spell_correction is True +@pytest.mark.asyncio +async def test_create_agent_async_from_dict(): + await test_create_agent_async(request_type=dict) + + def test_create_agent_field_headers(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) @@ -1003,7 +1080,7 @@ def test_create_agent_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_agent), "__call__") as call: + with mock.patch.object(type(client.transport.create_agent), "__call__") as call: call.return_value = gcdc_agent.Agent() client.create_agent(request) @@ -1028,9 +1105,7 @@ async def test_create_agent_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_agent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_agent.Agent()) await client.create_agent(request) @@ -1049,7 +1124,7 @@ def test_create_agent_flattened(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_agent), "__call__") as call: + with mock.patch.object(type(client.transport.create_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_agent.Agent() @@ -1087,9 +1162,7 @@ async def test_create_agent_flattened_async(): client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_agent.Agent() @@ -1136,7 +1209,7 @@ def test_update_agent( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_agent), "__call__") as call: + with mock.patch.object(type(client.transport.update_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_agent.Agent( name="name_value", @@ -1159,6 +1232,7 @@ def test_update_agent( assert args[0] == gcdc_agent.UpdateAgentRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_agent.Agent) assert response.name == "name_value" @@ -1185,19 +1259,19 @@ def test_update_agent_from_dict(): @pytest.mark.asyncio -async def test_update_agent_async(transport: str = "grpc_asyncio"): +async def test_update_agent_async( + transport: str = "grpc_asyncio", request_type=gcdc_agent.UpdateAgentRequest +): client = AgentsAsyncClient( 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 = gcdc_agent.UpdateAgentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_agent.Agent( @@ -1219,7 +1293,7 @@ async def test_update_agent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_agent.UpdateAgentRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_agent.Agent) @@ -1243,6 +1317,11 @@ async def test_update_agent_async(transport: str = "grpc_asyncio"): assert response.enable_spell_correction is True +@pytest.mark.asyncio +async def test_update_agent_async_from_dict(): + await test_update_agent_async(request_type=dict) + + def test_update_agent_field_headers(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) @@ -1252,7 +1331,7 @@ def test_update_agent_field_headers(): request.agent.name = "agent.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_agent), "__call__") as call: + with mock.patch.object(type(client.transport.update_agent), "__call__") as call: call.return_value = gcdc_agent.Agent() client.update_agent(request) @@ -1277,9 +1356,7 @@ async def test_update_agent_field_headers_async(): request.agent.name = "agent.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_agent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_agent.Agent()) await client.update_agent(request) @@ -1298,7 +1375,7 @@ def test_update_agent_flattened(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_agent), "__call__") as call: + with mock.patch.object(type(client.transport.update_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_agent.Agent() @@ -1337,9 +1414,7 @@ async def test_update_agent_flattened_async(): client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_agent.Agent() @@ -1385,7 +1460,7 @@ def test_delete_agent(transport: str = "grpc", request_type=agent.DeleteAgentReq request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_agent), "__call__") as call: + with mock.patch.object(type(client.transport.delete_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1406,19 +1481,19 @@ def test_delete_agent_from_dict(): @pytest.mark.asyncio -async def test_delete_agent_async(transport: str = "grpc_asyncio"): +async def test_delete_agent_async( + transport: str = "grpc_asyncio", request_type=agent.DeleteAgentRequest +): client = AgentsAsyncClient( 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 = agent.DeleteAgentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1428,12 +1503,17 @@ async def test_delete_agent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == agent.DeleteAgentRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_delete_agent_async_from_dict(): + await test_delete_agent_async(request_type=dict) + + def test_delete_agent_field_headers(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) @@ -1443,7 +1523,7 @@ def test_delete_agent_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_agent), "__call__") as call: + with mock.patch.object(type(client.transport.delete_agent), "__call__") as call: call.return_value = None client.delete_agent(request) @@ -1468,9 +1548,7 @@ async def test_delete_agent_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_agent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) await client.delete_agent(request) @@ -1489,7 +1567,7 @@ def test_delete_agent_flattened(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_agent), "__call__") as call: + with mock.patch.object(type(client.transport.delete_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1521,9 +1599,7 @@ async def test_delete_agent_flattened_async(): client = AgentsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1562,7 +1638,7 @@ def test_export_agent(transport: str = "grpc", request_type=agent.ExportAgentReq request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.export_agent), "__call__") as call: + with mock.patch.object(type(client.transport.export_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/spam") @@ -1583,19 +1659,19 @@ def test_export_agent_from_dict(): @pytest.mark.asyncio -async def test_export_agent_async(transport: str = "grpc_asyncio"): +async def test_export_agent_async( + transport: str = "grpc_asyncio", request_type=agent.ExportAgentRequest +): client = AgentsAsyncClient( 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 = agent.ExportAgentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.export_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.export_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/spam") @@ -1607,12 +1683,17 @@ async def test_export_agent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == agent.ExportAgentRequest() # Establish that the response is the type that we expect. assert isinstance(response, future.Future) +@pytest.mark.asyncio +async def test_export_agent_async_from_dict(): + await test_export_agent_async(request_type=dict) + + def test_export_agent_field_headers(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) @@ -1622,7 +1703,7 @@ def test_export_agent_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.export_agent), "__call__") as call: + with mock.patch.object(type(client.transport.export_agent), "__call__") as call: call.return_value = operations_pb2.Operation(name="operations/op") client.export_agent(request) @@ -1647,9 +1728,7 @@ async def test_export_agent_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.export_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.export_agent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/op") ) @@ -1676,7 +1755,7 @@ def test_restore_agent(transport: str = "grpc", request_type=agent.RestoreAgentR request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.restore_agent), "__call__") as call: + with mock.patch.object(type(client.transport.restore_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/spam") @@ -1697,19 +1776,19 @@ def test_restore_agent_from_dict(): @pytest.mark.asyncio -async def test_restore_agent_async(transport: str = "grpc_asyncio"): +async def test_restore_agent_async( + transport: str = "grpc_asyncio", request_type=agent.RestoreAgentRequest +): client = AgentsAsyncClient( 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 = agent.RestoreAgentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.restore_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.restore_agent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/spam") @@ -1721,12 +1800,17 @@ async def test_restore_agent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == agent.RestoreAgentRequest() # Establish that the response is the type that we expect. assert isinstance(response, future.Future) +@pytest.mark.asyncio +async def test_restore_agent_async_from_dict(): + await test_restore_agent_async(request_type=dict) + + def test_restore_agent_field_headers(): client = AgentsClient(credentials=credentials.AnonymousCredentials(),) @@ -1736,7 +1820,7 @@ def test_restore_agent_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.restore_agent), "__call__") as call: + with mock.patch.object(type(client.transport.restore_agent), "__call__") as call: call.return_value = operations_pb2.Operation(name="operations/op") client.restore_agent(request) @@ -1761,9 +1845,7 @@ async def test_restore_agent_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.restore_agent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.restore_agent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/op") ) @@ -1816,7 +1898,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = AgentsClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -1834,10 +1916,22 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], +) +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 = AgentsClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.AgentsGrpcTransport,) + assert isinstance(client.transport, transports.AgentsGrpcTransport,) def test_agents_base_transport_error(): @@ -1902,6 +1996,17 @@ def test_agents_base_transport_with_credentials_file(): ) +def test_agents_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.dialogflowcx_v3beta1.services.agents.transports.AgentsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.AgentsTransport() + adc.assert_called_once() + + def test_agents_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -1940,7 +2045,7 @@ def test_agents_host_no_port(): api_endpoint="dialogflow.googleapis.com" ), ) - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_agents_host_with_port(): @@ -1950,204 +2055,124 @@ def test_agents_host_with_port(): api_endpoint="dialogflow.googleapis.com:8000" ), ) - assert client._transport._host == "dialogflow.googleapis.com:8000" + assert client.transport._host == "dialogflow.googleapis.com:8000" def test_agents_grpc_transport_channel(): channel = grpc.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.AgentsGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_agents_grpc_asyncio_transport_channel(): channel = aio.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.AgentsGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_agents_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.AgentsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_agents_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.AgentsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == None @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_agents_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.AgentsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel +def test_agents_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_agents_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials class. +def test_agents_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), ): - mock_cred = mock.Mock() - transport = transports.AgentsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + with mock.patch.object( + transport_class, "create_channel", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel def test_agents_grpc_lro_client(): client = AgentsClient( credentials=credentials.AnonymousCredentials(), transport="grpc", ) - transport = client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsClient,) @@ -2160,7 +2185,7 @@ def test_agents_grpc_lro_async_client(): client = AgentsAsyncClient( credentials=credentials.AnonymousCredentials(), transport="grpc_asyncio", ) - transport = client._client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) @@ -2194,6 +2219,134 @@ def test_parse_agent_path(): assert expected == actual +def test_flow_path(): + project = "cuttlefish" + location = "mussel" + agent = "winkle" + flow = "nautilus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, + ) + actual = AgentsClient.flow_path(project, location, agent, flow) + assert expected == actual + + +def test_parse_flow_path(): + expected = { + "project": "scallop", + "location": "abalone", + "agent": "squid", + "flow": "clam", + } + path = AgentsClient.flow_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_flow_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "whelk" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = AgentsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "octopus", + } + path = AgentsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "oyster" + + expected = "folders/{folder}".format(folder=folder,) + actual = AgentsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nudibranch", + } + path = AgentsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "cuttlefish" + + expected = "organizations/{organization}".format(organization=organization,) + actual = AgentsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "mussel", + } + path = AgentsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "winkle" + + expected = "projects/{project}".format(project=project,) + actual = AgentsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nautilus", + } + path = AgentsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "scallop" + location = "abalone" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = AgentsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "squid", + "location": "clam", + } + path = AgentsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = AgentsClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo() diff --git a/tests/unit/gapic/dialogflowcx_v3beta1/test_entity_types.py b/tests/unit/gapic/dialogflowcx_v3beta1/test_entity_types.py index a59effb6..e08b0e64 100644 --- a/tests/unit/gapic/dialogflowcx_v3beta1/test_entity_types.py +++ b/tests/unit/gapic/dialogflowcx_v3beta1/test_entity_types.py @@ -93,12 +93,12 @@ def test_entity_types_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_entity_types_client_get_transport_class(): @@ -152,15 +152,14 @@ def test_entity_types_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -169,15 +168,14 @@ def test_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -186,95 +184,173 @@ def test_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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, + ssl_channel_credentials=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", + [ + (EntityTypesClient, transports.EntityTypesGrpcTransport, "grpc", "true"), + ( + EntityTypesAsyncClient, + transports.EntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (EntityTypesClient, transports.EntityTypesGrpcTransport, "grpc", "false"), + ( + EntityTypesAsyncClient, + transports.EntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + EntityTypesClient, "DEFAULT_ENDPOINT", modify_default_endpoint(EntityTypesClient) +) +@mock.patch.object( + EntityTypesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(EntityTypesAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_entity_types_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) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): - with mock.patch.object(transport_class, "__init__") as patched: + ssl_channel_creds = mock.Mock() with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds ): patched.return_value = None - client = client_class() + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=expected_ssl_channel_creds, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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=False, + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=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 has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - 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, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) @pytest.mark.parametrize( @@ -301,8 +377,7 @@ def test_entity_types_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -332,8 +407,7 @@ def test_entity_types_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -350,8 +424,7 @@ def test_entity_types_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -370,7 +443,7 @@ def test_list_entity_types( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_entity_types), "__call__" + type(client.transport.list_entity_types), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = entity_type.ListEntityTypesResponse( @@ -386,6 +459,7 @@ def test_list_entity_types( assert args[0] == entity_type.ListEntityTypesRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListEntityTypesPager) assert response.next_page_token == "next_page_token_value" @@ -396,18 +470,20 @@ def test_list_entity_types_from_dict(): @pytest.mark.asyncio -async def test_list_entity_types_async(transport: str = "grpc_asyncio"): +async def test_list_entity_types_async( + transport: str = "grpc_asyncio", request_type=entity_type.ListEntityTypesRequest +): client = EntityTypesAsyncClient( 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 = entity_type.ListEntityTypesRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_entity_types), "__call__" + type(client.transport.list_entity_types), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -422,7 +498,7 @@ async def test_list_entity_types_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == entity_type.ListEntityTypesRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListEntityTypesAsyncPager) @@ -430,6 +506,11 @@ async def test_list_entity_types_async(transport: str = "grpc_asyncio"): assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_list_entity_types_async_from_dict(): + await test_list_entity_types_async(request_type=dict) + + def test_list_entity_types_field_headers(): client = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) @@ -440,7 +521,7 @@ def test_list_entity_types_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_entity_types), "__call__" + type(client.transport.list_entity_types), "__call__" ) as call: call.return_value = entity_type.ListEntityTypesResponse() @@ -467,7 +548,7 @@ async def test_list_entity_types_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_entity_types), "__call__" + type(client.transport.list_entity_types), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( entity_type.ListEntityTypesResponse() @@ -490,7 +571,7 @@ def test_list_entity_types_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_entity_types), "__call__" + type(client.transport.list_entity_types), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = entity_type.ListEntityTypesResponse() @@ -524,7 +605,7 @@ async def test_list_entity_types_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_entity_types), "__call__" + type(client.transport.list_entity_types), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = entity_type.ListEntityTypesResponse() @@ -561,7 +642,7 @@ def test_list_entity_types_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_entity_types), "__call__" + type(client.transport.list_entity_types), "__call__" ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -603,7 +684,7 @@ def test_list_entity_types_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_entity_types), "__call__" + type(client.transport.list_entity_types), "__call__" ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -637,7 +718,7 @@ async def test_list_entity_types_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_entity_types), + type(client.transport.list_entity_types), "__call__", new_callable=mock.AsyncMock, ) as call: @@ -678,7 +759,7 @@ async def test_list_entity_types_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_entity_types), + type(client.transport.list_entity_types), "__call__", new_callable=mock.AsyncMock, ) as call: @@ -722,7 +803,7 @@ def test_get_entity_type( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_entity_type), "__call__") as call: + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = entity_type.EntityType( name="name_value", @@ -741,6 +822,7 @@ def test_get_entity_type( assert args[0] == entity_type.GetEntityTypeRequest() # Establish that the response is the type that we expect. + assert isinstance(response, entity_type.EntityType) assert response.name == "name_value" @@ -762,19 +844,19 @@ def test_get_entity_type_from_dict(): @pytest.mark.asyncio -async def test_get_entity_type_async(transport: str = "grpc_asyncio"): +async def test_get_entity_type_async( + transport: str = "grpc_asyncio", request_type=entity_type.GetEntityTypeRequest +): client = EntityTypesAsyncClient( 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 = entity_type.GetEntityTypeRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_entity_type), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( entity_type.EntityType( @@ -792,7 +874,7 @@ async def test_get_entity_type_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == entity_type.GetEntityTypeRequest() # Establish that the response is the type that we expect. assert isinstance(response, entity_type.EntityType) @@ -811,6 +893,11 @@ async def test_get_entity_type_async(transport: str = "grpc_asyncio"): assert response.enable_fuzzy_extraction is True +@pytest.mark.asyncio +async def test_get_entity_type_async_from_dict(): + await test_get_entity_type_async(request_type=dict) + + def test_get_entity_type_field_headers(): client = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) @@ -820,7 +907,7 @@ def test_get_entity_type_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_entity_type), "__call__") as call: + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: call.return_value = entity_type.EntityType() client.get_entity_type(request) @@ -845,9 +932,7 @@ async def test_get_entity_type_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_entity_type), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( entity_type.EntityType() ) @@ -868,7 +953,7 @@ def test_get_entity_type_flattened(): client = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_entity_type), "__call__") as call: + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = entity_type.EntityType() @@ -900,9 +985,7 @@ async def test_get_entity_type_flattened_async(): client = EntityTypesAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_entity_type), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = entity_type.EntityType() @@ -946,7 +1029,7 @@ def test_create_entity_type( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_entity_type), "__call__" + type(client.transport.create_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_entity_type.EntityType( @@ -966,6 +1049,7 @@ def test_create_entity_type( assert args[0] == gcdc_entity_type.CreateEntityTypeRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_entity_type.EntityType) assert response.name == "name_value" @@ -987,18 +1071,21 @@ def test_create_entity_type_from_dict(): @pytest.mark.asyncio -async def test_create_entity_type_async(transport: str = "grpc_asyncio"): +async def test_create_entity_type_async( + transport: str = "grpc_asyncio", + request_type=gcdc_entity_type.CreateEntityTypeRequest, +): client = EntityTypesAsyncClient( 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 = gcdc_entity_type.CreateEntityTypeRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_entity_type), "__call__" + type(client.transport.create_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -1017,7 +1104,7 @@ async def test_create_entity_type_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_entity_type.CreateEntityTypeRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_entity_type.EntityType) @@ -1036,6 +1123,11 @@ async def test_create_entity_type_async(transport: str = "grpc_asyncio"): assert response.enable_fuzzy_extraction is True +@pytest.mark.asyncio +async def test_create_entity_type_async_from_dict(): + await test_create_entity_type_async(request_type=dict) + + def test_create_entity_type_field_headers(): client = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) @@ -1046,7 +1138,7 @@ def test_create_entity_type_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_entity_type), "__call__" + type(client.transport.create_entity_type), "__call__" ) as call: call.return_value = gcdc_entity_type.EntityType() @@ -1073,7 +1165,7 @@ async def test_create_entity_type_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_entity_type), "__call__" + type(client.transport.create_entity_type), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_entity_type.EntityType() @@ -1096,7 +1188,7 @@ def test_create_entity_type_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_entity_type), "__call__" + type(client.transport.create_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_entity_type.EntityType() @@ -1137,7 +1229,7 @@ async def test_create_entity_type_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_entity_type), "__call__" + type(client.transport.create_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_entity_type.EntityType() @@ -1189,7 +1281,7 @@ def test_update_entity_type( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_entity_type), "__call__" + type(client.transport.update_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_entity_type.EntityType( @@ -1209,6 +1301,7 @@ def test_update_entity_type( assert args[0] == gcdc_entity_type.UpdateEntityTypeRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_entity_type.EntityType) assert response.name == "name_value" @@ -1230,18 +1323,21 @@ def test_update_entity_type_from_dict(): @pytest.mark.asyncio -async def test_update_entity_type_async(transport: str = "grpc_asyncio"): +async def test_update_entity_type_async( + transport: str = "grpc_asyncio", + request_type=gcdc_entity_type.UpdateEntityTypeRequest, +): client = EntityTypesAsyncClient( 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 = gcdc_entity_type.UpdateEntityTypeRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_entity_type), "__call__" + type(client.transport.update_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -1260,7 +1356,7 @@ async def test_update_entity_type_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_entity_type.UpdateEntityTypeRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_entity_type.EntityType) @@ -1279,6 +1375,11 @@ async def test_update_entity_type_async(transport: str = "grpc_asyncio"): assert response.enable_fuzzy_extraction is True +@pytest.mark.asyncio +async def test_update_entity_type_async_from_dict(): + await test_update_entity_type_async(request_type=dict) + + def test_update_entity_type_field_headers(): client = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) @@ -1289,7 +1390,7 @@ def test_update_entity_type_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_entity_type), "__call__" + type(client.transport.update_entity_type), "__call__" ) as call: call.return_value = gcdc_entity_type.EntityType() @@ -1318,7 +1419,7 @@ async def test_update_entity_type_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_entity_type), "__call__" + type(client.transport.update_entity_type), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_entity_type.EntityType() @@ -1343,7 +1444,7 @@ def test_update_entity_type_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_entity_type), "__call__" + type(client.transport.update_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_entity_type.EntityType() @@ -1384,7 +1485,7 @@ async def test_update_entity_type_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_entity_type), "__call__" + type(client.transport.update_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_entity_type.EntityType() @@ -1436,7 +1537,7 @@ def test_delete_entity_type( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_entity_type), "__call__" + type(client.transport.delete_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1458,18 +1559,20 @@ def test_delete_entity_type_from_dict(): @pytest.mark.asyncio -async def test_delete_entity_type_async(transport: str = "grpc_asyncio"): +async def test_delete_entity_type_async( + transport: str = "grpc_asyncio", request_type=entity_type.DeleteEntityTypeRequest +): client = EntityTypesAsyncClient( 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 = entity_type.DeleteEntityTypeRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_entity_type), "__call__" + type(client.transport.delete_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1480,12 +1583,17 @@ async def test_delete_entity_type_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == entity_type.DeleteEntityTypeRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_delete_entity_type_async_from_dict(): + await test_delete_entity_type_async(request_type=dict) + + def test_delete_entity_type_field_headers(): client = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) @@ -1496,7 +1604,7 @@ def test_delete_entity_type_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_entity_type), "__call__" + type(client.transport.delete_entity_type), "__call__" ) as call: call.return_value = None @@ -1523,7 +1631,7 @@ async def test_delete_entity_type_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_entity_type), "__call__" + type(client.transport.delete_entity_type), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1544,7 +1652,7 @@ def test_delete_entity_type_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_entity_type), "__call__" + type(client.transport.delete_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1578,7 +1686,7 @@ async def test_delete_entity_type_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_entity_type), "__call__" + type(client.transport.delete_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1644,7 +1752,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = EntityTypesClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -1662,10 +1770,22 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], +) +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 = EntityTypesClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.EntityTypesGrpcTransport,) + assert isinstance(client.transport, transports.EntityTypesGrpcTransport,) def test_entity_types_base_transport_error(): @@ -1723,6 +1843,17 @@ def test_entity_types_base_transport_with_credentials_file(): ) +def test_entity_types_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.dialogflowcx_v3beta1.services.entity_types.transports.EntityTypesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.EntityTypesTransport() + adc.assert_called_once() + + def test_entity_types_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -1761,7 +1892,7 @@ def test_entity_types_host_no_port(): api_endpoint="dialogflow.googleapis.com" ), ) - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_entity_types_host_with_port(): @@ -1771,197 +1902,117 @@ def test_entity_types_host_with_port(): api_endpoint="dialogflow.googleapis.com:8000" ), ) - assert client._transport._host == "dialogflow.googleapis.com:8000" + assert client.transport._host == "dialogflow.googleapis.com:8000" def test_entity_types_grpc_transport_channel(): channel = grpc.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.EntityTypesGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_entity_types_grpc_asyncio_transport_channel(): channel = aio.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.EntityTypesGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_entity_types_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.EntityTypesGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_entity_types_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.EntityTypesGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == None @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_entity_types_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.EntityTypesGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel +def test_entity_types_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_entity_types_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials class. +def test_entity_types_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), ): - mock_cred = mock.Mock() - transport = transports.EntityTypesGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + with mock.patch.object( + transport_class, "create_channel", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel def test_entity_type_path(): @@ -1991,6 +2042,107 @@ def test_parse_entity_type_path(): assert expected == actual +def test_common_billing_account_path(): + billing_account = "winkle" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = EntityTypesClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nautilus", + } + path = EntityTypesClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = EntityTypesClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "scallop" + + expected = "folders/{folder}".format(folder=folder,) + actual = EntityTypesClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "abalone", + } + path = EntityTypesClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = EntityTypesClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "squid" + + expected = "organizations/{organization}".format(organization=organization,) + actual = EntityTypesClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "clam", + } + path = EntityTypesClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = EntityTypesClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "whelk" + + expected = "projects/{project}".format(project=project,) + actual = EntityTypesClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "octopus", + } + path = EntityTypesClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = EntityTypesClient.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 = EntityTypesClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + } + path = EntityTypesClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = EntityTypesClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo() diff --git a/tests/unit/gapic/dialogflowcx_v3beta1/test_environments.py b/tests/unit/gapic/dialogflowcx_v3beta1/test_environments.py index a4b40220..07b0315b 100644 --- a/tests/unit/gapic/dialogflowcx_v3beta1/test_environments.py +++ b/tests/unit/gapic/dialogflowcx_v3beta1/test_environments.py @@ -31,7 +31,7 @@ from google.api_core import gapic_v1 from google.api_core import grpc_helpers from google.api_core import grpc_helpers_async -from google.api_core import operation_async +from google.api_core import operation_async # type: ignore from google.api_core import operations_v1 from google.auth import credentials from google.auth.exceptions import MutualTLSChannelError @@ -99,12 +99,12 @@ def test_environments_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_environments_client_get_transport_class(): @@ -158,15 +158,14 @@ def test_environments_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -175,15 +174,14 @@ def test_environments_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -192,95 +190,173 @@ def test_environments_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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, + ssl_channel_credentials=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", + [ + (EnvironmentsClient, transports.EnvironmentsGrpcTransport, "grpc", "true"), + ( + EnvironmentsAsyncClient, + transports.EnvironmentsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (EnvironmentsClient, transports.EnvironmentsGrpcTransport, "grpc", "false"), + ( + EnvironmentsAsyncClient, + transports.EnvironmentsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + EnvironmentsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(EnvironmentsClient) +) +@mock.patch.object( + EnvironmentsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(EnvironmentsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_environments_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) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): - with mock.patch.object(transport_class, "__init__") as patched: + ssl_channel_creds = mock.Mock() with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds ): patched.return_value = None - client = client_class() + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=expected_ssl_channel_creds, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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=False, + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=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 has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - 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, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) @pytest.mark.parametrize( @@ -307,8 +383,7 @@ def test_environments_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -338,8 +413,7 @@ def test_environments_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -356,8 +430,7 @@ def test_environments_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -376,7 +449,7 @@ def test_list_environments( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_environments), "__call__" + type(client.transport.list_environments), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = environment.ListEnvironmentsResponse( @@ -392,6 +465,7 @@ def test_list_environments( assert args[0] == environment.ListEnvironmentsRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListEnvironmentsPager) assert response.next_page_token == "next_page_token_value" @@ -402,18 +476,20 @@ def test_list_environments_from_dict(): @pytest.mark.asyncio -async def test_list_environments_async(transport: str = "grpc_asyncio"): +async def test_list_environments_async( + transport: str = "grpc_asyncio", request_type=environment.ListEnvironmentsRequest +): client = EnvironmentsAsyncClient( 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 = environment.ListEnvironmentsRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_environments), "__call__" + type(client.transport.list_environments), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -428,7 +504,7 @@ async def test_list_environments_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == environment.ListEnvironmentsRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListEnvironmentsAsyncPager) @@ -436,6 +512,11 @@ async def test_list_environments_async(transport: str = "grpc_asyncio"): assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_list_environments_async_from_dict(): + await test_list_environments_async(request_type=dict) + + def test_list_environments_field_headers(): client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) @@ -446,7 +527,7 @@ def test_list_environments_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_environments), "__call__" + type(client.transport.list_environments), "__call__" ) as call: call.return_value = environment.ListEnvironmentsResponse() @@ -473,7 +554,7 @@ async def test_list_environments_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_environments), "__call__" + type(client.transport.list_environments), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( environment.ListEnvironmentsResponse() @@ -496,7 +577,7 @@ def test_list_environments_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_environments), "__call__" + type(client.transport.list_environments), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = environment.ListEnvironmentsResponse() @@ -530,7 +611,7 @@ async def test_list_environments_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_environments), "__call__" + type(client.transport.list_environments), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = environment.ListEnvironmentsResponse() @@ -567,7 +648,7 @@ def test_list_environments_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_environments), "__call__" + type(client.transport.list_environments), "__call__" ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -609,7 +690,7 @@ def test_list_environments_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_environments), "__call__" + type(client.transport.list_environments), "__call__" ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -643,7 +724,7 @@ async def test_list_environments_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_environments), + type(client.transport.list_environments), "__call__", new_callable=mock.AsyncMock, ) as call: @@ -684,7 +765,7 @@ async def test_list_environments_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_environments), + type(client.transport.list_environments), "__call__", new_callable=mock.AsyncMock, ) as call: @@ -728,7 +809,7 @@ def test_get_environment( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_environment), "__call__") as call: + with mock.patch.object(type(client.transport.get_environment), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = environment.Environment( name="name_value", @@ -745,6 +826,7 @@ def test_get_environment( assert args[0] == environment.GetEnvironmentRequest() # Establish that the response is the type that we expect. + assert isinstance(response, environment.Environment) assert response.name == "name_value" @@ -759,19 +841,19 @@ def test_get_environment_from_dict(): @pytest.mark.asyncio -async def test_get_environment_async(transport: str = "grpc_asyncio"): +async def test_get_environment_async( + transport: str = "grpc_asyncio", request_type=environment.GetEnvironmentRequest +): client = EnvironmentsAsyncClient( 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 = environment.GetEnvironmentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_environment), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_environment), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( environment.Environment( @@ -787,7 +869,7 @@ async def test_get_environment_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == environment.GetEnvironmentRequest() # Establish that the response is the type that we expect. assert isinstance(response, environment.Environment) @@ -799,6 +881,11 @@ async def test_get_environment_async(transport: str = "grpc_asyncio"): assert response.description == "description_value" +@pytest.mark.asyncio +async def test_get_environment_async_from_dict(): + await test_get_environment_async(request_type=dict) + + def test_get_environment_field_headers(): client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) @@ -808,7 +895,7 @@ def test_get_environment_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_environment), "__call__") as call: + with mock.patch.object(type(client.transport.get_environment), "__call__") as call: call.return_value = environment.Environment() client.get_environment(request) @@ -833,9 +920,7 @@ async def test_get_environment_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_environment), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_environment), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( environment.Environment() ) @@ -856,7 +941,7 @@ def test_get_environment_flattened(): client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_environment), "__call__") as call: + with mock.patch.object(type(client.transport.get_environment), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = environment.Environment() @@ -888,9 +973,7 @@ async def test_get_environment_flattened_async(): client = EnvironmentsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_environment), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_environment), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = environment.Environment() @@ -934,7 +1017,7 @@ def test_create_environment( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_environment), "__call__" + type(client.transport.create_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/spam") @@ -956,18 +1039,21 @@ def test_create_environment_from_dict(): @pytest.mark.asyncio -async def test_create_environment_async(transport: str = "grpc_asyncio"): +async def test_create_environment_async( + transport: str = "grpc_asyncio", + request_type=gcdc_environment.CreateEnvironmentRequest, +): client = EnvironmentsAsyncClient( 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 = gcdc_environment.CreateEnvironmentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_environment), "__call__" + type(client.transport.create_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -980,12 +1066,17 @@ async def test_create_environment_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_environment.CreateEnvironmentRequest() # Establish that the response is the type that we expect. assert isinstance(response, future.Future) +@pytest.mark.asyncio +async def test_create_environment_async_from_dict(): + await test_create_environment_async(request_type=dict) + + def test_create_environment_field_headers(): client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) @@ -996,7 +1087,7 @@ def test_create_environment_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_environment), "__call__" + type(client.transport.create_environment), "__call__" ) as call: call.return_value = operations_pb2.Operation(name="operations/op") @@ -1023,7 +1114,7 @@ async def test_create_environment_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_environment), "__call__" + type(client.transport.create_environment), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/op") @@ -1046,7 +1137,7 @@ def test_create_environment_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_environment), "__call__" + type(client.transport.create_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1087,7 +1178,7 @@ async def test_create_environment_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_environment), "__call__" + type(client.transport.create_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1139,7 +1230,7 @@ def test_update_environment( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_environment), "__call__" + type(client.transport.update_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/spam") @@ -1161,18 +1252,21 @@ def test_update_environment_from_dict(): @pytest.mark.asyncio -async def test_update_environment_async(transport: str = "grpc_asyncio"): +async def test_update_environment_async( + transport: str = "grpc_asyncio", + request_type=gcdc_environment.UpdateEnvironmentRequest, +): client = EnvironmentsAsyncClient( 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 = gcdc_environment.UpdateEnvironmentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_environment), "__call__" + type(client.transport.update_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -1185,12 +1279,17 @@ async def test_update_environment_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_environment.UpdateEnvironmentRequest() # Establish that the response is the type that we expect. assert isinstance(response, future.Future) +@pytest.mark.asyncio +async def test_update_environment_async_from_dict(): + await test_update_environment_async(request_type=dict) + + def test_update_environment_field_headers(): client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) @@ -1201,7 +1300,7 @@ def test_update_environment_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_environment), "__call__" + type(client.transport.update_environment), "__call__" ) as call: call.return_value = operations_pb2.Operation(name="operations/op") @@ -1230,7 +1329,7 @@ async def test_update_environment_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_environment), "__call__" + type(client.transport.update_environment), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/op") @@ -1255,7 +1354,7 @@ def test_update_environment_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_environment), "__call__" + type(client.transport.update_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1296,7 +1395,7 @@ async def test_update_environment_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_environment), "__call__" + type(client.transport.update_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1348,7 +1447,7 @@ def test_delete_environment( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_environment), "__call__" + type(client.transport.delete_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1370,18 +1469,20 @@ def test_delete_environment_from_dict(): @pytest.mark.asyncio -async def test_delete_environment_async(transport: str = "grpc_asyncio"): +async def test_delete_environment_async( + transport: str = "grpc_asyncio", request_type=environment.DeleteEnvironmentRequest +): client = EnvironmentsAsyncClient( 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 = environment.DeleteEnvironmentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_environment), "__call__" + type(client.transport.delete_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1392,12 +1493,17 @@ async def test_delete_environment_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == environment.DeleteEnvironmentRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_delete_environment_async_from_dict(): + await test_delete_environment_async(request_type=dict) + + def test_delete_environment_field_headers(): client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) @@ -1408,7 +1514,7 @@ def test_delete_environment_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_environment), "__call__" + type(client.transport.delete_environment), "__call__" ) as call: call.return_value = None @@ -1435,7 +1541,7 @@ async def test_delete_environment_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_environment), "__call__" + type(client.transport.delete_environment), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1456,7 +1562,7 @@ def test_delete_environment_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_environment), "__call__" + type(client.transport.delete_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1490,7 +1596,7 @@ async def test_delete_environment_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_environment), "__call__" + type(client.transport.delete_environment), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1533,7 +1639,7 @@ def test_lookup_environment_history( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.lookup_environment_history), "__call__" + type(client.transport.lookup_environment_history), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = environment.LookupEnvironmentHistoryResponse( @@ -1549,6 +1655,7 @@ def test_lookup_environment_history( assert args[0] == environment.LookupEnvironmentHistoryRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.LookupEnvironmentHistoryPager) assert response.next_page_token == "next_page_token_value" @@ -1559,18 +1666,21 @@ def test_lookup_environment_history_from_dict(): @pytest.mark.asyncio -async def test_lookup_environment_history_async(transport: str = "grpc_asyncio"): +async def test_lookup_environment_history_async( + transport: str = "grpc_asyncio", + request_type=environment.LookupEnvironmentHistoryRequest, +): client = EnvironmentsAsyncClient( 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 = environment.LookupEnvironmentHistoryRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.lookup_environment_history), "__call__" + type(client.transport.lookup_environment_history), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -1585,7 +1695,7 @@ async def test_lookup_environment_history_async(transport: str = "grpc_asyncio") assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == environment.LookupEnvironmentHistoryRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.LookupEnvironmentHistoryAsyncPager) @@ -1593,6 +1703,11 @@ async def test_lookup_environment_history_async(transport: str = "grpc_asyncio") assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_lookup_environment_history_async_from_dict(): + await test_lookup_environment_history_async(request_type=dict) + + def test_lookup_environment_history_field_headers(): client = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) @@ -1603,7 +1718,7 @@ def test_lookup_environment_history_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.lookup_environment_history), "__call__" + type(client.transport.lookup_environment_history), "__call__" ) as call: call.return_value = environment.LookupEnvironmentHistoryResponse() @@ -1630,7 +1745,7 @@ async def test_lookup_environment_history_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.lookup_environment_history), "__call__" + type(client.transport.lookup_environment_history), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( environment.LookupEnvironmentHistoryResponse() @@ -1653,7 +1768,7 @@ def test_lookup_environment_history_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.lookup_environment_history), "__call__" + type(client.transport.lookup_environment_history), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = environment.LookupEnvironmentHistoryResponse() @@ -1687,7 +1802,7 @@ async def test_lookup_environment_history_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.lookup_environment_history), "__call__" + type(client.transport.lookup_environment_history), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = environment.LookupEnvironmentHistoryResponse() @@ -1724,7 +1839,7 @@ def test_lookup_environment_history_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.lookup_environment_history), "__call__" + type(client.transport.lookup_environment_history), "__call__" ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -1766,7 +1881,7 @@ def test_lookup_environment_history_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.lookup_environment_history), "__call__" + type(client.transport.lookup_environment_history), "__call__" ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -1800,7 +1915,7 @@ async def test_lookup_environment_history_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.lookup_environment_history), + type(client.transport.lookup_environment_history), "__call__", new_callable=mock.AsyncMock, ) as call: @@ -1841,7 +1956,7 @@ async def test_lookup_environment_history_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.lookup_environment_history), + type(client.transport.lookup_environment_history), "__call__", new_callable=mock.AsyncMock, ) as call: @@ -1909,7 +2024,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = EnvironmentsClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -1927,10 +2042,22 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], +) +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 = EnvironmentsClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.EnvironmentsGrpcTransport,) + assert isinstance(client.transport, transports.EnvironmentsGrpcTransport,) def test_environments_base_transport_error(): @@ -1994,6 +2121,17 @@ def test_environments_base_transport_with_credentials_file(): ) +def test_environments_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.dialogflowcx_v3beta1.services.environments.transports.EnvironmentsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.EnvironmentsTransport() + adc.assert_called_once() + + def test_environments_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -2032,7 +2170,7 @@ def test_environments_host_no_port(): api_endpoint="dialogflow.googleapis.com" ), ) - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_environments_host_with_port(): @@ -2042,204 +2180,124 @@ def test_environments_host_with_port(): api_endpoint="dialogflow.googleapis.com:8000" ), ) - assert client._transport._host == "dialogflow.googleapis.com:8000" + assert client.transport._host == "dialogflow.googleapis.com:8000" def test_environments_grpc_transport_channel(): channel = grpc.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.EnvironmentsGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_environments_grpc_asyncio_transport_channel(): channel = aio.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.EnvironmentsGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_environments_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.EnvironmentsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_environments_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.EnvironmentsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == None @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_environments_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.EnvironmentsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel +def test_environments_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_environments_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials class. +def test_environments_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), ): - mock_cred = mock.Mock() - transport = transports.EnvironmentsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + with mock.patch.object( + transport_class, "create_channel", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel def test_environments_grpc_lro_client(): client = EnvironmentsClient( credentials=credentials.AnonymousCredentials(), transport="grpc", ) - transport = client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsClient,) @@ -2252,7 +2310,7 @@ def test_environments_grpc_lro_async_client(): client = EnvironmentsAsyncClient( credentials=credentials.AnonymousCredentials(), transport="grpc_asyncio", ) - transport = client._client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) @@ -2288,6 +2346,136 @@ def test_parse_environment_path(): assert expected == actual +def test_version_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + flow = "abalone" + version = "squid" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/versions/{version}".format( + project=project, location=location, agent=agent, flow=flow, version=version, + ) + actual = EnvironmentsClient.version_path(project, location, agent, flow, version) + assert expected == actual + + +def test_parse_version_path(): + expected = { + "project": "clam", + "location": "whelk", + "agent": "octopus", + "flow": "oyster", + "version": "nudibranch", + } + path = EnvironmentsClient.version_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_version_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "cuttlefish" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = EnvironmentsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "mussel", + } + path = EnvironmentsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "winkle" + + expected = "folders/{folder}".format(folder=folder,) + actual = EnvironmentsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nautilus", + } + path = EnvironmentsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "scallop" + + expected = "organizations/{organization}".format(organization=organization,) + actual = EnvironmentsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "abalone", + } + path = EnvironmentsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "squid" + + expected = "projects/{project}".format(project=project,) + actual = EnvironmentsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "clam", + } + path = EnvironmentsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "whelk" + location = "octopus" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = EnvironmentsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + } + path = EnvironmentsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = EnvironmentsClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo() diff --git a/tests/unit/gapic/dialogflowcx_v3beta1/test_flows.py b/tests/unit/gapic/dialogflowcx_v3beta1/test_flows.py index 9a202da3..3b7366c2 100644 --- a/tests/unit/gapic/dialogflowcx_v3beta1/test_flows.py +++ b/tests/unit/gapic/dialogflowcx_v3beta1/test_flows.py @@ -31,7 +31,7 @@ from google.api_core import gapic_v1 from google.api_core import grpc_helpers from google.api_core import grpc_helpers_async -from google.api_core import operation_async +from google.api_core import operation_async # type: ignore from google.api_core import operations_v1 from google.auth import credentials from google.auth.exceptions import MutualTLSChannelError @@ -43,7 +43,6 @@ from google.cloud.dialogflowcx_v3beta1.types import flow as gcdc_flow from google.cloud.dialogflowcx_v3beta1.types import fulfillment from google.cloud.dialogflowcx_v3beta1.types import page -from google.cloud.dialogflowcx_v3beta1.types import page as gcdc_page from google.cloud.dialogflowcx_v3beta1.types import response_message from google.longrunning import operations_pb2 from google.oauth2 import service_account @@ -97,12 +96,12 @@ def test_flows_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_flows_client_get_transport_class(): @@ -148,15 +147,14 @@ def test_flows_client_client_options(client_class, transport_class, transport_na credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -165,15 +163,14 @@ def test_flows_client_client_options(client_class, transport_class, transport_na credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -182,95 +179,171 @@ def test_flows_client_client_options(client_class, transport_class, transport_na credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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, + ssl_channel_credentials=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", + [ + (FlowsClient, transports.FlowsGrpcTransport, "grpc", "true"), + ( + FlowsAsyncClient, + transports.FlowsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (FlowsClient, transports.FlowsGrpcTransport, "grpc", "false"), + ( + FlowsAsyncClient, + transports.FlowsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + FlowsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(FlowsClient) +) +@mock.patch.object( + FlowsAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(FlowsAsyncClient) +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_flows_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) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): - with mock.patch.object(transport_class, "__init__") as patched: + ssl_channel_creds = mock.Mock() with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds ): patched.return_value = None - client = client_class() + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=expected_ssl_channel_creds, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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=False, + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=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 has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - 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, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) @pytest.mark.parametrize( @@ -293,8 +366,7 @@ def test_flows_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -320,8 +392,7 @@ def test_flows_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -338,8 +409,7 @@ def test_flows_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -355,7 +425,7 @@ def test_create_flow(transport: str = "grpc", request_type=gcdc_flow.CreateFlowR request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_flow), "__call__") as call: + with mock.patch.object(type(client.transport.create_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_flow.Flow( name="name_value", @@ -372,6 +442,7 @@ def test_create_flow(transport: str = "grpc", request_type=gcdc_flow.CreateFlowR assert args[0] == gcdc_flow.CreateFlowRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_flow.Flow) assert response.name == "name_value" @@ -386,19 +457,19 @@ def test_create_flow_from_dict(): @pytest.mark.asyncio -async def test_create_flow_async(transport: str = "grpc_asyncio"): +async def test_create_flow_async( + transport: str = "grpc_asyncio", request_type=gcdc_flow.CreateFlowRequest +): client = FlowsAsyncClient( 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 = gcdc_flow.CreateFlowRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_flow.Flow( @@ -414,7 +485,7 @@ async def test_create_flow_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_flow.CreateFlowRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_flow.Flow) @@ -426,6 +497,11 @@ async def test_create_flow_async(transport: str = "grpc_asyncio"): assert response.description == "description_value" +@pytest.mark.asyncio +async def test_create_flow_async_from_dict(): + await test_create_flow_async(request_type=dict) + + def test_create_flow_field_headers(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) @@ -435,7 +511,7 @@ def test_create_flow_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_flow), "__call__") as call: + with mock.patch.object(type(client.transport.create_flow), "__call__") as call: call.return_value = gcdc_flow.Flow() client.create_flow(request) @@ -460,9 +536,7 @@ async def test_create_flow_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_flow), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_flow.Flow()) await client.create_flow(request) @@ -481,7 +555,7 @@ def test_create_flow_flattened(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_flow), "__call__") as call: + with mock.patch.object(type(client.transport.create_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_flow.Flow() @@ -519,9 +593,7 @@ async def test_create_flow_flattened_async(): client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_flow.Flow() @@ -566,7 +638,7 @@ def test_delete_flow(transport: str = "grpc", request_type=flow.DeleteFlowReques request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_flow), "__call__") as call: + with mock.patch.object(type(client.transport.delete_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -587,19 +659,19 @@ def test_delete_flow_from_dict(): @pytest.mark.asyncio -async def test_delete_flow_async(transport: str = "grpc_asyncio"): +async def test_delete_flow_async( + transport: str = "grpc_asyncio", request_type=flow.DeleteFlowRequest +): client = FlowsAsyncClient( 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 = flow.DeleteFlowRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -609,12 +681,17 @@ async def test_delete_flow_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == flow.DeleteFlowRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_delete_flow_async_from_dict(): + await test_delete_flow_async(request_type=dict) + + def test_delete_flow_field_headers(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) @@ -624,7 +701,7 @@ def test_delete_flow_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_flow), "__call__") as call: + with mock.patch.object(type(client.transport.delete_flow), "__call__") as call: call.return_value = None client.delete_flow(request) @@ -649,9 +726,7 @@ async def test_delete_flow_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_flow), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) await client.delete_flow(request) @@ -670,7 +745,7 @@ def test_delete_flow_flattened(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_flow), "__call__") as call: + with mock.patch.object(type(client.transport.delete_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -702,9 +777,7 @@ async def test_delete_flow_flattened_async(): client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -743,7 +816,7 @@ def test_list_flows(transport: str = "grpc", request_type=flow.ListFlowsRequest) request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_flows), "__call__") as call: + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = flow.ListFlowsResponse( next_page_token="next_page_token_value", @@ -758,6 +831,7 @@ def test_list_flows(transport: str = "grpc", request_type=flow.ListFlowsRequest) assert args[0] == flow.ListFlowsRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListFlowsPager) assert response.next_page_token == "next_page_token_value" @@ -768,19 +842,19 @@ def test_list_flows_from_dict(): @pytest.mark.asyncio -async def test_list_flows_async(transport: str = "grpc_asyncio"): +async def test_list_flows_async( + transport: str = "grpc_asyncio", request_type=flow.ListFlowsRequest +): client = FlowsAsyncClient( 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 = flow.ListFlowsRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_flows), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( flow.ListFlowsResponse(next_page_token="next_page_token_value",) @@ -792,7 +866,7 @@ async def test_list_flows_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == flow.ListFlowsRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListFlowsAsyncPager) @@ -800,6 +874,11 @@ async def test_list_flows_async(transport: str = "grpc_asyncio"): assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_list_flows_async_from_dict(): + await test_list_flows_async(request_type=dict) + + def test_list_flows_field_headers(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) @@ -809,7 +888,7 @@ def test_list_flows_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_flows), "__call__") as call: + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: call.return_value = flow.ListFlowsResponse() client.list_flows(request) @@ -834,9 +913,7 @@ async def test_list_flows_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_flows), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( flow.ListFlowsResponse() ) @@ -857,7 +934,7 @@ def test_list_flows_flattened(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_flows), "__call__") as call: + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = flow.ListFlowsResponse() @@ -889,9 +966,7 @@ async def test_list_flows_flattened_async(): client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_flows), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = flow.ListFlowsResponse() @@ -926,7 +1001,7 @@ def test_list_flows_pager(): client = FlowsClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_flows), "__call__") as call: + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( flow.ListFlowsResponse( @@ -955,7 +1030,7 @@ def test_list_flows_pages(): client = FlowsClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_flows), "__call__") as call: + with mock.patch.object(type(client.transport.list_flows), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( flow.ListFlowsResponse( @@ -977,9 +1052,7 @@ async def test_list_flows_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_flows), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_flows), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -1007,9 +1080,7 @@ async def test_list_flows_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_flows), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_flows), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -1038,7 +1109,7 @@ def test_get_flow(transport: str = "grpc", request_type=flow.GetFlowRequest): request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_flow), "__call__") as call: + with mock.patch.object(type(client.transport.get_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = flow.Flow( name="name_value", @@ -1055,6 +1126,7 @@ def test_get_flow(transport: str = "grpc", request_type=flow.GetFlowRequest): assert args[0] == flow.GetFlowRequest() # Establish that the response is the type that we expect. + assert isinstance(response, flow.Flow) assert response.name == "name_value" @@ -1069,19 +1141,19 @@ def test_get_flow_from_dict(): @pytest.mark.asyncio -async def test_get_flow_async(transport: str = "grpc_asyncio"): +async def test_get_flow_async( + transport: str = "grpc_asyncio", request_type=flow.GetFlowRequest +): client = FlowsAsyncClient( 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 = flow.GetFlowRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( flow.Flow( @@ -1097,7 +1169,7 @@ async def test_get_flow_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == flow.GetFlowRequest() # Establish that the response is the type that we expect. assert isinstance(response, flow.Flow) @@ -1109,6 +1181,11 @@ async def test_get_flow_async(transport: str = "grpc_asyncio"): assert response.description == "description_value" +@pytest.mark.asyncio +async def test_get_flow_async_from_dict(): + await test_get_flow_async(request_type=dict) + + def test_get_flow_field_headers(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) @@ -1118,7 +1195,7 @@ def test_get_flow_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_flow), "__call__") as call: + with mock.patch.object(type(client.transport.get_flow), "__call__") as call: call.return_value = flow.Flow() client.get_flow(request) @@ -1143,9 +1220,7 @@ async def test_get_flow_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_flow), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(flow.Flow()) await client.get_flow(request) @@ -1164,7 +1239,7 @@ def test_get_flow_flattened(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_flow), "__call__") as call: + with mock.patch.object(type(client.transport.get_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = flow.Flow() @@ -1196,9 +1271,7 @@ async def test_get_flow_flattened_async(): client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = flow.Flow() @@ -1237,7 +1310,7 @@ def test_update_flow(transport: str = "grpc", request_type=gcdc_flow.UpdateFlowR request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_flow), "__call__") as call: + with mock.patch.object(type(client.transport.update_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_flow.Flow( name="name_value", @@ -1254,6 +1327,7 @@ def test_update_flow(transport: str = "grpc", request_type=gcdc_flow.UpdateFlowR assert args[0] == gcdc_flow.UpdateFlowRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_flow.Flow) assert response.name == "name_value" @@ -1268,19 +1342,19 @@ def test_update_flow_from_dict(): @pytest.mark.asyncio -async def test_update_flow_async(transport: str = "grpc_asyncio"): +async def test_update_flow_async( + transport: str = "grpc_asyncio", request_type=gcdc_flow.UpdateFlowRequest +): client = FlowsAsyncClient( 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 = gcdc_flow.UpdateFlowRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_flow.Flow( @@ -1296,7 +1370,7 @@ async def test_update_flow_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_flow.UpdateFlowRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_flow.Flow) @@ -1308,6 +1382,11 @@ async def test_update_flow_async(transport: str = "grpc_asyncio"): assert response.description == "description_value" +@pytest.mark.asyncio +async def test_update_flow_async_from_dict(): + await test_update_flow_async(request_type=dict) + + def test_update_flow_field_headers(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) @@ -1317,7 +1396,7 @@ def test_update_flow_field_headers(): request.flow.name = "flow.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_flow), "__call__") as call: + with mock.patch.object(type(client.transport.update_flow), "__call__") as call: call.return_value = gcdc_flow.Flow() client.update_flow(request) @@ -1342,9 +1421,7 @@ async def test_update_flow_field_headers_async(): request.flow.name = "flow.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_flow), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_flow.Flow()) await client.update_flow(request) @@ -1363,7 +1440,7 @@ def test_update_flow_flattened(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_flow), "__call__") as call: + with mock.patch.object(type(client.transport.update_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_flow.Flow() @@ -1402,9 +1479,7 @@ async def test_update_flow_flattened_async(): client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_flow.Flow() @@ -1450,7 +1525,7 @@ def test_train_flow(transport: str = "grpc", request_type=flow.TrainFlowRequest) request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.train_flow), "__call__") as call: + with mock.patch.object(type(client.transport.train_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/spam") @@ -1471,19 +1546,19 @@ def test_train_flow_from_dict(): @pytest.mark.asyncio -async def test_train_flow_async(transport: str = "grpc_asyncio"): +async def test_train_flow_async( + transport: str = "grpc_asyncio", request_type=flow.TrainFlowRequest +): client = FlowsAsyncClient( 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 = flow.TrainFlowRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.train_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.train_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/spam") @@ -1495,12 +1570,17 @@ async def test_train_flow_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == flow.TrainFlowRequest() # Establish that the response is the type that we expect. assert isinstance(response, future.Future) +@pytest.mark.asyncio +async def test_train_flow_async_from_dict(): + await test_train_flow_async(request_type=dict) + + def test_train_flow_field_headers(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) @@ -1510,7 +1590,7 @@ def test_train_flow_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.train_flow), "__call__") as call: + with mock.patch.object(type(client.transport.train_flow), "__call__") as call: call.return_value = operations_pb2.Operation(name="operations/op") client.train_flow(request) @@ -1535,9 +1615,7 @@ async def test_train_flow_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.train_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.train_flow), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/op") ) @@ -1558,7 +1636,7 @@ def test_train_flow_flattened(): client = FlowsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.train_flow), "__call__") as call: + with mock.patch.object(type(client.transport.train_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1590,9 +1668,7 @@ async def test_train_flow_flattened_async(): client = FlowsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.train_flow), "__call__" - ) as call: + with mock.patch.object(type(client.transport.train_flow), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1659,7 +1735,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = FlowsClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -1677,10 +1753,22 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [transports.FlowsGrpcTransport, transports.FlowsGrpcAsyncIOTransport], +) +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 = FlowsClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.FlowsGrpcTransport,) + assert isinstance(client.transport, transports.FlowsGrpcTransport,) def test_flows_base_transport_error(): @@ -1744,6 +1832,17 @@ def test_flows_base_transport_with_credentials_file(): ) +def test_flows_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.dialogflowcx_v3beta1.services.flows.transports.FlowsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.FlowsTransport() + adc.assert_called_once() + + def test_flows_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -1782,7 +1881,7 @@ def test_flows_host_no_port(): api_endpoint="dialogflow.googleapis.com" ), ) - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_flows_host_with_port(): @@ -1792,204 +1891,122 @@ def test_flows_host_with_port(): api_endpoint="dialogflow.googleapis.com:8000" ), ) - assert client._transport._host == "dialogflow.googleapis.com:8000" + assert client.transport._host == "dialogflow.googleapis.com:8000" def test_flows_grpc_transport_channel(): channel = grpc.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() - transport = transports.FlowsGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, - ) + # Check that channel is used if provided. + transport = transports.FlowsGrpcTransport(host="squid.clam.whelk", channel=channel,) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_flows_grpc_asyncio_transport_channel(): channel = aio.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.FlowsGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_flows_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.FlowsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_flows_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.FlowsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == None @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.FlowsGrpcTransport, transports.FlowsGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_flows_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.FlowsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel +def test_flows_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.FlowsGrpcTransport, transports.FlowsGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_flows_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials class. +def test_flows_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), ): - mock_cred = mock.Mock() - transport = transports.FlowsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + with mock.patch.object( + transport_class, "create_channel", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel def test_flows_grpc_lro_client(): client = FlowsClient( credentials=credentials.AnonymousCredentials(), transport="grpc", ) - transport = client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsClient,) @@ -2002,7 +2019,7 @@ def test_flows_grpc_lro_async_client(): client = FlowsAsyncClient( credentials=credentials.AnonymousCredentials(), transport="grpc_asyncio", ) - transport = client._client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) @@ -2038,6 +2055,190 @@ def test_parse_flow_path(): assert expected == actual +def test_intent_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + intent = "abalone" + + expected = "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + actual = FlowsClient.intent_path(project, location, agent, intent) + assert expected == actual + + +def test_parse_intent_path(): + expected = { + "project": "squid", + "location": "clam", + "agent": "whelk", + "intent": "octopus", + } + path = FlowsClient.intent_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_intent_path(path) + assert expected == actual + + +def test_page_path(): + project = "oyster" + location = "nudibranch" + agent = "cuttlefish" + flow = "mussel" + page = "winkle" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + actual = FlowsClient.page_path(project, location, agent, flow, page) + assert expected == actual + + +def test_parse_page_path(): + expected = { + "project": "nautilus", + "location": "scallop", + "agent": "abalone", + "flow": "squid", + "page": "clam", + } + path = FlowsClient.page_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_page_path(path) + assert expected == actual + + +def test_webhook_path(): + project = "whelk" + location = "octopus" + agent = "oyster" + webhook = "nudibranch" + + expected = "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + actual = FlowsClient.webhook_path(project, location, agent, webhook) + assert expected == actual + + +def test_parse_webhook_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + "agent": "winkle", + "webhook": "nautilus", + } + path = FlowsClient.webhook_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_webhook_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "scallop" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = FlowsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "abalone", + } + path = FlowsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "squid" + + expected = "folders/{folder}".format(folder=folder,) + actual = FlowsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "clam", + } + path = FlowsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "whelk" + + expected = "organizations/{organization}".format(organization=organization,) + actual = FlowsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "octopus", + } + path = FlowsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "oyster" + + expected = "projects/{project}".format(project=project,) + actual = FlowsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nudibranch", + } + path = FlowsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "cuttlefish" + location = "mussel" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = FlowsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "winkle", + "location": "nautilus", + } + path = FlowsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = FlowsClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo() diff --git a/tests/unit/gapic/dialogflowcx_v3beta1/test_intents.py b/tests/unit/gapic/dialogflowcx_v3beta1/test_intents.py index 5039aaae..3845195d 100644 --- a/tests/unit/gapic/dialogflowcx_v3beta1/test_intents.py +++ b/tests/unit/gapic/dialogflowcx_v3beta1/test_intents.py @@ -88,12 +88,12 @@ def test_intents_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_intents_client_get_transport_class(): @@ -139,15 +139,14 @@ def test_intents_client_client_options(client_class, transport_class, transport_ credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -156,15 +155,14 @@ def test_intents_client_client_options(client_class, transport_class, transport_ credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -173,95 +171,171 @@ def test_intents_client_client_options(client_class, transport_class, transport_ credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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, + ssl_channel_credentials=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", + [ + (IntentsClient, transports.IntentsGrpcTransport, "grpc", "true"), + ( + IntentsAsyncClient, + transports.IntentsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (IntentsClient, transports.IntentsGrpcTransport, "grpc", "false"), + ( + IntentsAsyncClient, + transports.IntentsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + IntentsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(IntentsClient) +) +@mock.patch.object( + IntentsAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(IntentsAsyncClient) +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_intents_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) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): - with mock.patch.object(transport_class, "__init__") as patched: + ssl_channel_creds = mock.Mock() with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds ): patched.return_value = None - client = client_class() + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=expected_ssl_channel_creds, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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=False, + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=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 has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - 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, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) @pytest.mark.parametrize( @@ -284,8 +358,7 @@ def test_intents_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -311,8 +384,7 @@ def test_intents_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -329,8 +401,7 @@ def test_intents_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -346,7 +417,7 @@ def test_list_intents(transport: str = "grpc", request_type=intent.ListIntentsRe request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_intents), "__call__") as call: + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = intent.ListIntentsResponse( next_page_token="next_page_token_value", @@ -361,6 +432,7 @@ def test_list_intents(transport: str = "grpc", request_type=intent.ListIntentsRe assert args[0] == intent.ListIntentsRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListIntentsPager) assert response.next_page_token == "next_page_token_value" @@ -371,19 +443,19 @@ def test_list_intents_from_dict(): @pytest.mark.asyncio -async def test_list_intents_async(transport: str = "grpc_asyncio"): +async def test_list_intents_async( + transport: str = "grpc_asyncio", request_type=intent.ListIntentsRequest +): client = IntentsAsyncClient( 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 = intent.ListIntentsRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_intents), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( intent.ListIntentsResponse(next_page_token="next_page_token_value",) @@ -395,7 +467,7 @@ async def test_list_intents_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == intent.ListIntentsRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListIntentsAsyncPager) @@ -403,6 +475,11 @@ async def test_list_intents_async(transport: str = "grpc_asyncio"): assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_list_intents_async_from_dict(): + await test_list_intents_async(request_type=dict) + + def test_list_intents_field_headers(): client = IntentsClient(credentials=credentials.AnonymousCredentials(),) @@ -412,7 +489,7 @@ def test_list_intents_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_intents), "__call__") as call: + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: call.return_value = intent.ListIntentsResponse() client.list_intents(request) @@ -437,9 +514,7 @@ async def test_list_intents_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_intents), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( intent.ListIntentsResponse() ) @@ -460,7 +535,7 @@ def test_list_intents_flattened(): client = IntentsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_intents), "__call__") as call: + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = intent.ListIntentsResponse() @@ -492,9 +567,7 @@ async def test_list_intents_flattened_async(): client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_intents), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = intent.ListIntentsResponse() @@ -529,7 +602,7 @@ def test_list_intents_pager(): client = IntentsClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_intents), "__call__") as call: + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( intent.ListIntentsResponse( @@ -561,7 +634,7 @@ def test_list_intents_pages(): client = IntentsClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_intents), "__call__") as call: + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( intent.ListIntentsResponse( @@ -586,9 +659,7 @@ async def test_list_intents_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_intents), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_intents), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -619,9 +690,7 @@ async def test_list_intents_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_intents), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_intents), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -653,13 +722,14 @@ def test_get_intent(transport: str = "grpc", request_type=intent.GetIntentReques request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_intent), "__call__") as call: + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = intent.Intent( name="name_value", display_name="display_name_value", priority=898, is_fallback=True, + description="description_value", ) response = client.get_intent(request) @@ -671,6 +741,7 @@ def test_get_intent(transport: str = "grpc", request_type=intent.GetIntentReques assert args[0] == intent.GetIntentRequest() # Establish that the response is the type that we expect. + assert isinstance(response, intent.Intent) assert response.name == "name_value" @@ -681,25 +752,27 @@ def test_get_intent(transport: str = "grpc", request_type=intent.GetIntentReques assert response.is_fallback is True + assert response.description == "description_value" + def test_get_intent_from_dict(): test_get_intent(request_type=dict) @pytest.mark.asyncio -async def test_get_intent_async(transport: str = "grpc_asyncio"): +async def test_get_intent_async( + transport: str = "grpc_asyncio", request_type=intent.GetIntentRequest +): client = IntentsAsyncClient( 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 = intent.GetIntentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( intent.Intent( @@ -707,6 +780,7 @@ async def test_get_intent_async(transport: str = "grpc_asyncio"): display_name="display_name_value", priority=898, is_fallback=True, + description="description_value", ) ) @@ -716,7 +790,7 @@ async def test_get_intent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == intent.GetIntentRequest() # Establish that the response is the type that we expect. assert isinstance(response, intent.Intent) @@ -729,6 +803,13 @@ async def test_get_intent_async(transport: str = "grpc_asyncio"): assert response.is_fallback is True + assert response.description == "description_value" + + +@pytest.mark.asyncio +async def test_get_intent_async_from_dict(): + await test_get_intent_async(request_type=dict) + def test_get_intent_field_headers(): client = IntentsClient(credentials=credentials.AnonymousCredentials(),) @@ -739,7 +820,7 @@ def test_get_intent_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_intent), "__call__") as call: + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: call.return_value = intent.Intent() client.get_intent(request) @@ -764,9 +845,7 @@ async def test_get_intent_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(intent.Intent()) await client.get_intent(request) @@ -785,7 +864,7 @@ def test_get_intent_flattened(): client = IntentsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_intent), "__call__") as call: + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = intent.Intent() @@ -817,9 +896,7 @@ async def test_get_intent_flattened_async(): client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = intent.Intent() @@ -860,13 +937,14 @@ def test_create_intent( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_intent), "__call__") as call: + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_intent.Intent( name="name_value", display_name="display_name_value", priority=898, is_fallback=True, + description="description_value", ) response = client.create_intent(request) @@ -878,6 +956,7 @@ def test_create_intent( assert args[0] == gcdc_intent.CreateIntentRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_intent.Intent) assert response.name == "name_value" @@ -888,25 +967,27 @@ def test_create_intent( assert response.is_fallback is True + assert response.description == "description_value" + def test_create_intent_from_dict(): test_create_intent(request_type=dict) @pytest.mark.asyncio -async def test_create_intent_async(transport: str = "grpc_asyncio"): +async def test_create_intent_async( + transport: str = "grpc_asyncio", request_type=gcdc_intent.CreateIntentRequest +): client = IntentsAsyncClient( 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 = gcdc_intent.CreateIntentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_intent.Intent( @@ -914,6 +995,7 @@ async def test_create_intent_async(transport: str = "grpc_asyncio"): display_name="display_name_value", priority=898, is_fallback=True, + description="description_value", ) ) @@ -923,7 +1005,7 @@ async def test_create_intent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_intent.CreateIntentRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_intent.Intent) @@ -936,6 +1018,13 @@ async def test_create_intent_async(transport: str = "grpc_asyncio"): assert response.is_fallback is True + assert response.description == "description_value" + + +@pytest.mark.asyncio +async def test_create_intent_async_from_dict(): + await test_create_intent_async(request_type=dict) + def test_create_intent_field_headers(): client = IntentsClient(credentials=credentials.AnonymousCredentials(),) @@ -946,7 +1035,7 @@ def test_create_intent_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_intent), "__call__") as call: + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: call.return_value = gcdc_intent.Intent() client.create_intent(request) @@ -971,9 +1060,7 @@ async def test_create_intent_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_intent.Intent()) await client.create_intent(request) @@ -992,7 +1079,7 @@ def test_create_intent_flattened(): client = IntentsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_intent), "__call__") as call: + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_intent.Intent() @@ -1030,9 +1117,7 @@ async def test_create_intent_flattened_async(): client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_intent.Intent() @@ -1079,13 +1164,14 @@ def test_update_intent( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_intent), "__call__") as call: + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_intent.Intent( name="name_value", display_name="display_name_value", priority=898, is_fallback=True, + description="description_value", ) response = client.update_intent(request) @@ -1097,6 +1183,7 @@ def test_update_intent( assert args[0] == gcdc_intent.UpdateIntentRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_intent.Intent) assert response.name == "name_value" @@ -1107,25 +1194,27 @@ def test_update_intent( assert response.is_fallback is True + assert response.description == "description_value" + def test_update_intent_from_dict(): test_update_intent(request_type=dict) @pytest.mark.asyncio -async def test_update_intent_async(transport: str = "grpc_asyncio"): +async def test_update_intent_async( + transport: str = "grpc_asyncio", request_type=gcdc_intent.UpdateIntentRequest +): client = IntentsAsyncClient( 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 = gcdc_intent.UpdateIntentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_intent.Intent( @@ -1133,6 +1222,7 @@ async def test_update_intent_async(transport: str = "grpc_asyncio"): display_name="display_name_value", priority=898, is_fallback=True, + description="description_value", ) ) @@ -1142,7 +1232,7 @@ async def test_update_intent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_intent.UpdateIntentRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_intent.Intent) @@ -1155,6 +1245,13 @@ async def test_update_intent_async(transport: str = "grpc_asyncio"): assert response.is_fallback is True + assert response.description == "description_value" + + +@pytest.mark.asyncio +async def test_update_intent_async_from_dict(): + await test_update_intent_async(request_type=dict) + def test_update_intent_field_headers(): client = IntentsClient(credentials=credentials.AnonymousCredentials(),) @@ -1165,7 +1262,7 @@ def test_update_intent_field_headers(): request.intent.name = "intent.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_intent), "__call__") as call: + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: call.return_value = gcdc_intent.Intent() client.update_intent(request) @@ -1190,9 +1287,7 @@ async def test_update_intent_field_headers_async(): request.intent.name = "intent.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_intent.Intent()) await client.update_intent(request) @@ -1211,7 +1306,7 @@ def test_update_intent_flattened(): client = IntentsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_intent), "__call__") as call: + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_intent.Intent() @@ -1250,9 +1345,7 @@ async def test_update_intent_flattened_async(): client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_intent.Intent() @@ -1300,7 +1393,7 @@ def test_delete_intent( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_intent), "__call__") as call: + with mock.patch.object(type(client.transport.delete_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1321,19 +1414,19 @@ def test_delete_intent_from_dict(): @pytest.mark.asyncio -async def test_delete_intent_async(transport: str = "grpc_asyncio"): +async def test_delete_intent_async( + transport: str = "grpc_asyncio", request_type=intent.DeleteIntentRequest +): client = IntentsAsyncClient( 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 = intent.DeleteIntentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1343,12 +1436,17 @@ async def test_delete_intent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == intent.DeleteIntentRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_delete_intent_async_from_dict(): + await test_delete_intent_async(request_type=dict) + + def test_delete_intent_field_headers(): client = IntentsClient(credentials=credentials.AnonymousCredentials(),) @@ -1358,7 +1456,7 @@ def test_delete_intent_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_intent), "__call__") as call: + with mock.patch.object(type(client.transport.delete_intent), "__call__") as call: call.return_value = None client.delete_intent(request) @@ -1383,9 +1481,7 @@ async def test_delete_intent_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_intent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) await client.delete_intent(request) @@ -1404,7 +1500,7 @@ def test_delete_intent_flattened(): client = IntentsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_intent), "__call__") as call: + with mock.patch.object(type(client.transport.delete_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1436,9 +1532,7 @@ async def test_delete_intent_flattened_async(): client = IntentsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1503,7 +1597,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = IntentsClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -1521,10 +1615,22 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], +) +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 = IntentsClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.IntentsGrpcTransport,) + assert isinstance(client.transport, transports.IntentsGrpcTransport,) def test_intents_base_transport_error(): @@ -1582,6 +1688,17 @@ def test_intents_base_transport_with_credentials_file(): ) +def test_intents_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.dialogflowcx_v3beta1.services.intents.transports.IntentsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.IntentsTransport() + adc.assert_called_once() + + def test_intents_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -1620,7 +1737,7 @@ def test_intents_host_no_port(): api_endpoint="dialogflow.googleapis.com" ), ) - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_intents_host_with_port(): @@ -1630,204 +1747,151 @@ def test_intents_host_with_port(): api_endpoint="dialogflow.googleapis.com:8000" ), ) - assert client._transport._host == "dialogflow.googleapis.com:8000" + assert client.transport._host == "dialogflow.googleapis.com:8000" def test_intents_grpc_transport_channel(): channel = grpc.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.IntentsGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_intents_grpc_asyncio_transport_channel(): channel = aio.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.IntentsGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_intents_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.IntentsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_intents_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.IntentsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == None @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_intents_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.IntentsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel +def test_intents_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_intents_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials class. +def test_intents_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), ): - mock_cred = mock.Mock() - transport = transports.IntentsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + with mock.patch.object( + transport_class, "create_channel", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel -def test_intent_path(): + +def test_entity_type_path(): project = "squid" location = "clam" agent = "whelk" - intent = "octopus" + entity_type = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, + ) + actual = IntentsClient.entity_type_path(project, location, agent, entity_type) + assert expected == actual + + +def test_parse_entity_type_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "entity_type": "mussel", + } + path = IntentsClient.entity_type_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_entity_type_path(path) + assert expected == actual + + +def test_intent_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + intent = "abalone" expected = "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( project=project, location=location, agent=agent, intent=intent, @@ -1838,10 +1902,10 @@ def test_intent_path(): def test_parse_intent_path(): expected = { - "project": "oyster", - "location": "nudibranch", - "agent": "cuttlefish", - "intent": "mussel", + "project": "squid", + "location": "clam", + "agent": "whelk", + "intent": "octopus", } path = IntentsClient.intent_path(**expected) @@ -1850,6 +1914,107 @@ def test_parse_intent_path(): assert expected == actual +def test_common_billing_account_path(): + billing_account = "oyster" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = IntentsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nudibranch", + } + path = IntentsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "cuttlefish" + + expected = "folders/{folder}".format(folder=folder,) + actual = IntentsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "mussel", + } + path = IntentsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "winkle" + + expected = "organizations/{organization}".format(organization=organization,) + actual = IntentsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nautilus", + } + path = IntentsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "scallop" + + expected = "projects/{project}".format(project=project,) + actual = IntentsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "abalone", + } + path = IntentsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "squid" + location = "clam" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = IntentsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "whelk", + "location": "octopus", + } + path = IntentsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = IntentsClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo() diff --git a/tests/unit/gapic/dialogflowcx_v3beta1/test_pages.py b/tests/unit/gapic/dialogflowcx_v3beta1/test_pages.py index 7611eba2..91ed58d2 100644 --- a/tests/unit/gapic/dialogflowcx_v3beta1/test_pages.py +++ b/tests/unit/gapic/dialogflowcx_v3beta1/test_pages.py @@ -91,12 +91,12 @@ def test_pages_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_pages_client_get_transport_class(): @@ -142,15 +142,14 @@ def test_pages_client_client_options(client_class, transport_class, transport_na credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -159,15 +158,14 @@ def test_pages_client_client_options(client_class, transport_class, transport_na credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -176,95 +174,171 @@ def test_pages_client_client_options(client_class, transport_class, transport_na credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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, + ssl_channel_credentials=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", + [ + (PagesClient, transports.PagesGrpcTransport, "grpc", "true"), + ( + PagesAsyncClient, + transports.PagesGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (PagesClient, transports.PagesGrpcTransport, "grpc", "false"), + ( + PagesAsyncClient, + transports.PagesGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + PagesClient, "DEFAULT_ENDPOINT", modify_default_endpoint(PagesClient) +) +@mock.patch.object( + PagesAsyncClient, "DEFAULT_ENDPOINT", modify_default_endpoint(PagesAsyncClient) +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_pages_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) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): - with mock.patch.object(transport_class, "__init__") as patched: + ssl_channel_creds = mock.Mock() with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds ): patched.return_value = None - client = client_class() + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=expected_ssl_channel_creds, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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=False, + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=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 has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - 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, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) @pytest.mark.parametrize( @@ -287,8 +361,7 @@ def test_pages_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -314,8 +387,7 @@ def test_pages_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -332,8 +404,7 @@ def test_pages_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -349,7 +420,7 @@ def test_list_pages(transport: str = "grpc", request_type=page.ListPagesRequest) request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_pages), "__call__") as call: + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = page.ListPagesResponse( next_page_token="next_page_token_value", @@ -364,6 +435,7 @@ def test_list_pages(transport: str = "grpc", request_type=page.ListPagesRequest) assert args[0] == page.ListPagesRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListPagesPager) assert response.next_page_token == "next_page_token_value" @@ -374,19 +446,19 @@ def test_list_pages_from_dict(): @pytest.mark.asyncio -async def test_list_pages_async(transport: str = "grpc_asyncio"): +async def test_list_pages_async( + transport: str = "grpc_asyncio", request_type=page.ListPagesRequest +): client = PagesAsyncClient( 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 = page.ListPagesRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_pages), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( page.ListPagesResponse(next_page_token="next_page_token_value",) @@ -398,7 +470,7 @@ async def test_list_pages_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == page.ListPagesRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListPagesAsyncPager) @@ -406,6 +478,11 @@ async def test_list_pages_async(transport: str = "grpc_asyncio"): assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_list_pages_async_from_dict(): + await test_list_pages_async(request_type=dict) + + def test_list_pages_field_headers(): client = PagesClient(credentials=credentials.AnonymousCredentials(),) @@ -415,7 +492,7 @@ def test_list_pages_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_pages), "__call__") as call: + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: call.return_value = page.ListPagesResponse() client.list_pages(request) @@ -440,9 +517,7 @@ async def test_list_pages_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_pages), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( page.ListPagesResponse() ) @@ -463,7 +538,7 @@ def test_list_pages_flattened(): client = PagesClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_pages), "__call__") as call: + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = page.ListPagesResponse() @@ -495,9 +570,7 @@ async def test_list_pages_flattened_async(): client = PagesAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_pages), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = page.ListPagesResponse() @@ -532,7 +605,7 @@ def test_list_pages_pager(): client = PagesClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_pages), "__call__") as call: + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( page.ListPagesResponse( @@ -561,7 +634,7 @@ def test_list_pages_pages(): client = PagesClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_pages), "__call__") as call: + with mock.patch.object(type(client.transport.list_pages), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( page.ListPagesResponse( @@ -583,9 +656,7 @@ async def test_list_pages_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_pages), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_pages), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -613,9 +684,7 @@ async def test_list_pages_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_pages), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_pages), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -644,7 +713,7 @@ def test_get_page(transport: str = "grpc", request_type=page.GetPageRequest): request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_page), "__call__") as call: + with mock.patch.object(type(client.transport.get_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = page.Page( name="name_value", @@ -661,6 +730,7 @@ def test_get_page(transport: str = "grpc", request_type=page.GetPageRequest): assert args[0] == page.GetPageRequest() # Establish that the response is the type that we expect. + assert isinstance(response, page.Page) assert response.name == "name_value" @@ -675,19 +745,19 @@ def test_get_page_from_dict(): @pytest.mark.asyncio -async def test_get_page_async(transport: str = "grpc_asyncio"): +async def test_get_page_async( + transport: str = "grpc_asyncio", request_type=page.GetPageRequest +): client = PagesAsyncClient( 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 = page.GetPageRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( page.Page( @@ -703,7 +773,7 @@ async def test_get_page_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == page.GetPageRequest() # Establish that the response is the type that we expect. assert isinstance(response, page.Page) @@ -715,6 +785,11 @@ async def test_get_page_async(transport: str = "grpc_asyncio"): assert response.transition_route_groups == ["transition_route_groups_value"] +@pytest.mark.asyncio +async def test_get_page_async_from_dict(): + await test_get_page_async(request_type=dict) + + def test_get_page_field_headers(): client = PagesClient(credentials=credentials.AnonymousCredentials(),) @@ -724,7 +799,7 @@ def test_get_page_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_page), "__call__") as call: + with mock.patch.object(type(client.transport.get_page), "__call__") as call: call.return_value = page.Page() client.get_page(request) @@ -749,9 +824,7 @@ async def test_get_page_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_page), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(page.Page()) await client.get_page(request) @@ -770,7 +843,7 @@ def test_get_page_flattened(): client = PagesClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_page), "__call__") as call: + with mock.patch.object(type(client.transport.get_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = page.Page() @@ -802,9 +875,7 @@ async def test_get_page_flattened_async(): client = PagesAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = page.Page() @@ -843,7 +914,7 @@ def test_create_page(transport: str = "grpc", request_type=gcdc_page.CreatePageR request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_page), "__call__") as call: + with mock.patch.object(type(client.transport.create_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_page.Page( name="name_value", @@ -860,6 +931,7 @@ def test_create_page(transport: str = "grpc", request_type=gcdc_page.CreatePageR assert args[0] == gcdc_page.CreatePageRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_page.Page) assert response.name == "name_value" @@ -874,19 +946,19 @@ def test_create_page_from_dict(): @pytest.mark.asyncio -async def test_create_page_async(transport: str = "grpc_asyncio"): +async def test_create_page_async( + transport: str = "grpc_asyncio", request_type=gcdc_page.CreatePageRequest +): client = PagesAsyncClient( 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 = gcdc_page.CreatePageRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_page.Page( @@ -902,7 +974,7 @@ async def test_create_page_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_page.CreatePageRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_page.Page) @@ -914,6 +986,11 @@ async def test_create_page_async(transport: str = "grpc_asyncio"): assert response.transition_route_groups == ["transition_route_groups_value"] +@pytest.mark.asyncio +async def test_create_page_async_from_dict(): + await test_create_page_async(request_type=dict) + + def test_create_page_field_headers(): client = PagesClient(credentials=credentials.AnonymousCredentials(),) @@ -923,7 +1000,7 @@ def test_create_page_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_page), "__call__") as call: + with mock.patch.object(type(client.transport.create_page), "__call__") as call: call.return_value = gcdc_page.Page() client.create_page(request) @@ -948,9 +1025,7 @@ async def test_create_page_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_page), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_page.Page()) await client.create_page(request) @@ -969,7 +1044,7 @@ def test_create_page_flattened(): client = PagesClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_page), "__call__") as call: + with mock.patch.object(type(client.transport.create_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_page.Page() @@ -1007,9 +1082,7 @@ async def test_create_page_flattened_async(): client = PagesAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_page.Page() @@ -1054,7 +1127,7 @@ def test_update_page(transport: str = "grpc", request_type=gcdc_page.UpdatePageR request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_page), "__call__") as call: + with mock.patch.object(type(client.transport.update_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_page.Page( name="name_value", @@ -1071,6 +1144,7 @@ def test_update_page(transport: str = "grpc", request_type=gcdc_page.UpdatePageR assert args[0] == gcdc_page.UpdatePageRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_page.Page) assert response.name == "name_value" @@ -1085,19 +1159,19 @@ def test_update_page_from_dict(): @pytest.mark.asyncio -async def test_update_page_async(transport: str = "grpc_asyncio"): +async def test_update_page_async( + transport: str = "grpc_asyncio", request_type=gcdc_page.UpdatePageRequest +): client = PagesAsyncClient( 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 = gcdc_page.UpdatePageRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_page.Page( @@ -1113,7 +1187,7 @@ async def test_update_page_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_page.UpdatePageRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_page.Page) @@ -1125,6 +1199,11 @@ async def test_update_page_async(transport: str = "grpc_asyncio"): assert response.transition_route_groups == ["transition_route_groups_value"] +@pytest.mark.asyncio +async def test_update_page_async_from_dict(): + await test_update_page_async(request_type=dict) + + def test_update_page_field_headers(): client = PagesClient(credentials=credentials.AnonymousCredentials(),) @@ -1134,7 +1213,7 @@ def test_update_page_field_headers(): request.page.name = "page.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_page), "__call__") as call: + with mock.patch.object(type(client.transport.update_page), "__call__") as call: call.return_value = gcdc_page.Page() client.update_page(request) @@ -1159,9 +1238,7 @@ async def test_update_page_field_headers_async(): request.page.name = "page.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_page), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(gcdc_page.Page()) await client.update_page(request) @@ -1180,7 +1257,7 @@ def test_update_page_flattened(): client = PagesClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_page), "__call__") as call: + with mock.patch.object(type(client.transport.update_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_page.Page() @@ -1219,9 +1296,7 @@ async def test_update_page_flattened_async(): client = PagesAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_page.Page() @@ -1267,7 +1342,7 @@ def test_delete_page(transport: str = "grpc", request_type=page.DeletePageReques request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_page), "__call__") as call: + with mock.patch.object(type(client.transport.delete_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1288,19 +1363,19 @@ def test_delete_page_from_dict(): @pytest.mark.asyncio -async def test_delete_page_async(transport: str = "grpc_asyncio"): +async def test_delete_page_async( + transport: str = "grpc_asyncio", request_type=page.DeletePageRequest +): client = PagesAsyncClient( 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 = page.DeletePageRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1310,12 +1385,17 @@ async def test_delete_page_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == page.DeletePageRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_delete_page_async_from_dict(): + await test_delete_page_async(request_type=dict) + + def test_delete_page_field_headers(): client = PagesClient(credentials=credentials.AnonymousCredentials(),) @@ -1325,7 +1405,7 @@ def test_delete_page_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_page), "__call__") as call: + with mock.patch.object(type(client.transport.delete_page), "__call__") as call: call.return_value = None client.delete_page(request) @@ -1350,9 +1430,7 @@ async def test_delete_page_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_page), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) await client.delete_page(request) @@ -1371,7 +1449,7 @@ def test_delete_page_flattened(): client = PagesClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_page), "__call__") as call: + with mock.patch.object(type(client.transport.delete_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1403,9 +1481,7 @@ async def test_delete_page_flattened_async(): client = PagesAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_page), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_page), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1470,7 +1546,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = PagesClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -1488,10 +1564,22 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [transports.PagesGrpcTransport, transports.PagesGrpcAsyncIOTransport], +) +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 = PagesClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.PagesGrpcTransport,) + assert isinstance(client.transport, transports.PagesGrpcTransport,) def test_pages_base_transport_error(): @@ -1549,6 +1637,17 @@ def test_pages_base_transport_with_credentials_file(): ) +def test_pages_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.dialogflowcx_v3beta1.services.pages.transports.PagesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.PagesTransport() + adc.assert_called_once() + + def test_pages_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -1587,7 +1686,7 @@ def test_pages_host_no_port(): api_endpoint="dialogflow.googleapis.com" ), ) - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_pages_host_with_port(): @@ -1597,197 +1696,196 @@ def test_pages_host_with_port(): api_endpoint="dialogflow.googleapis.com:8000" ), ) - assert client._transport._host == "dialogflow.googleapis.com:8000" + assert client.transport._host == "dialogflow.googleapis.com:8000" def test_pages_grpc_transport_channel(): channel = grpc.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() - transport = transports.PagesGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, - ) + # Check that channel is used if provided. + transport = transports.PagesGrpcTransport(host="squid.clam.whelk", channel=channel,) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_pages_grpc_asyncio_transport_channel(): channel = aio.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.PagesGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_pages_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() +@pytest.mark.parametrize( + "transport_class", + [transports.PagesGrpcTransport, transports.PagesGrpcAsyncIOTransport], +) +def test_pages_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + +@pytest.mark.parametrize( + "transport_class", + [transports.PagesGrpcTransport, transports.PagesGrpcAsyncIOTransport], +) +def test_pages_transport_channel_mtls_with_adc(transport_class): mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred + 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", autospec=True + ) 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, + ) - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel + 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel - transport = transports.PagesGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, + +def test_entity_type_path(): + project = "squid" + location = "clam" + agent = "whelk" + entity_type = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, ) - assert transport.grpc_channel == mock_grpc_channel + actual = PagesClient.entity_type_path(project, location, agent, entity_type) + assert expected == actual -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_pages_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() +def test_parse_entity_type_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "entity_type": "mussel", + } + path = PagesClient.entity_type_path(**expected) - mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred + # Check that the path construction is reversible. + actual = PagesClient.parse_entity_type_path(path) + assert expected == actual - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - transport = transports.PagesGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, +def test_flow_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + flow = "abalone" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, ) - assert transport.grpc_channel == mock_grpc_channel + actual = PagesClient.flow_path(project, location, agent, flow) + assert expected == actual -@pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] -) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_pages_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel +def test_parse_flow_path(): + expected = { + "project": "squid", + "location": "clam", + "agent": "whelk", + "flow": "octopus", + } + path = PagesClient.flow_path(**expected) - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.PagesGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + # Check that the path construction is reversible. + actual = PagesClient.parse_flow_path(path) + assert expected == actual -@pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] -) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_pages_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel +def test_intent_path(): + project = "oyster" + location = "nudibranch" + agent = "cuttlefish" + intent = "mussel" - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.PagesGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + expected = "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + actual = PagesClient.intent_path(project, location, agent, intent) + assert expected == actual + + +def test_parse_intent_path(): + expected = { + "project": "winkle", + "location": "nautilus", + "agent": "scallop", + "intent": "abalone", + } + path = PagesClient.intent_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_intent_path(path) + assert expected == actual def test_page_path(): @@ -1819,6 +1917,169 @@ def test_parse_page_path(): assert expected == actual +def test_transition_route_group_path(): + project = "scallop" + location = "abalone" + agent = "squid" + flow = "clam" + transition_route_group = "whelk" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/transitionRouteGroups/{transition_route_group}".format( + project=project, + location=location, + agent=agent, + flow=flow, + transition_route_group=transition_route_group, + ) + actual = PagesClient.transition_route_group_path( + project, location, agent, flow, transition_route_group + ) + assert expected == actual + + +def test_parse_transition_route_group_path(): + expected = { + "project": "octopus", + "location": "oyster", + "agent": "nudibranch", + "flow": "cuttlefish", + "transition_route_group": "mussel", + } + path = PagesClient.transition_route_group_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_transition_route_group_path(path) + assert expected == actual + + +def test_webhook_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + webhook = "abalone" + + expected = "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + actual = PagesClient.webhook_path(project, location, agent, webhook) + assert expected == actual + + +def test_parse_webhook_path(): + expected = { + "project": "squid", + "location": "clam", + "agent": "whelk", + "webhook": "octopus", + } + path = PagesClient.webhook_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_webhook_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "oyster" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = PagesClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nudibranch", + } + path = PagesClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "cuttlefish" + + expected = "folders/{folder}".format(folder=folder,) + actual = PagesClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "mussel", + } + path = PagesClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "winkle" + + expected = "organizations/{organization}".format(organization=organization,) + actual = PagesClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nautilus", + } + path = PagesClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "scallop" + + expected = "projects/{project}".format(project=project,) + actual = PagesClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "abalone", + } + path = PagesClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "squid" + location = "clam" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = PagesClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "whelk", + "location": "octopus", + } + path = PagesClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = PagesClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo() diff --git a/tests/unit/gapic/dialogflowcx_v3beta1/test_session_entity_types.py b/tests/unit/gapic/dialogflowcx_v3beta1/test_session_entity_types.py index b9d4444c..8f4191d3 100644 --- a/tests/unit/gapic/dialogflowcx_v3beta1/test_session_entity_types.py +++ b/tests/unit/gapic/dialogflowcx_v3beta1/test_session_entity_types.py @@ -41,7 +41,6 @@ from google.cloud.dialogflowcx_v3beta1.services.session_entity_types import pagers from google.cloud.dialogflowcx_v3beta1.services.session_entity_types import transports from google.cloud.dialogflowcx_v3beta1.types import entity_type -from google.cloud.dialogflowcx_v3beta1.types import entity_type as gcdc_entity_type from google.cloud.dialogflowcx_v3beta1.types import session_entity_type from google.cloud.dialogflowcx_v3beta1.types import ( session_entity_type as gcdc_session_entity_type, @@ -105,12 +104,12 @@ def test_session_entity_types_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_session_entity_types_client_get_transport_class(): @@ -166,15 +165,14 @@ def test_session_entity_types_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -183,15 +181,14 @@ def test_session_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -200,95 +197,185 @@ def test_session_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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, + ssl_channel_credentials=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", + [ + ( + SessionEntityTypesClient, + transports.SessionEntityTypesGrpcTransport, + "grpc", + "true", + ), + ( + SessionEntityTypesAsyncClient, + transports.SessionEntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + ( + SessionEntityTypesClient, + transports.SessionEntityTypesGrpcTransport, + "grpc", + "false", + ), + ( + SessionEntityTypesAsyncClient, + transports.SessionEntityTypesGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + SessionEntityTypesClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SessionEntityTypesClient), +) +@mock.patch.object( + SessionEntityTypesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SessionEntityTypesAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_session_entity_types_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) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): - with mock.patch.object(transport_class, "__init__") as patched: + ssl_channel_creds = mock.Mock() with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds ): patched.return_value = None - client = client_class() + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=expected_ssl_channel_creds, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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=False, + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=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 has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - 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, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) @pytest.mark.parametrize( @@ -315,8 +402,7 @@ def test_session_entity_types_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -346,8 +432,7 @@ def test_session_entity_types_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -366,8 +451,7 @@ def test_session_entity_types_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -387,7 +471,7 @@ def test_list_session_entity_types( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_session_entity_types), "__call__" + type(client.transport.list_session_entity_types), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = session_entity_type.ListSessionEntityTypesResponse( @@ -403,6 +487,7 @@ def test_list_session_entity_types( assert args[0] == session_entity_type.ListSessionEntityTypesRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListSessionEntityTypesPager) assert response.next_page_token == "next_page_token_value" @@ -413,18 +498,21 @@ def test_list_session_entity_types_from_dict(): @pytest.mark.asyncio -async def test_list_session_entity_types_async(transport: str = "grpc_asyncio"): +async def test_list_session_entity_types_async( + transport: str = "grpc_asyncio", + request_type=session_entity_type.ListSessionEntityTypesRequest, +): client = SessionEntityTypesAsyncClient( 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 = session_entity_type.ListSessionEntityTypesRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_session_entity_types), "__call__" + type(client.transport.list_session_entity_types), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -439,7 +527,7 @@ async def test_list_session_entity_types_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == session_entity_type.ListSessionEntityTypesRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListSessionEntityTypesAsyncPager) @@ -447,6 +535,11 @@ async def test_list_session_entity_types_async(transport: str = "grpc_asyncio"): assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_list_session_entity_types_async_from_dict(): + await test_list_session_entity_types_async(request_type=dict) + + def test_list_session_entity_types_field_headers(): client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) @@ -457,7 +550,7 @@ def test_list_session_entity_types_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_session_entity_types), "__call__" + type(client.transport.list_session_entity_types), "__call__" ) as call: call.return_value = session_entity_type.ListSessionEntityTypesResponse() @@ -486,7 +579,7 @@ async def test_list_session_entity_types_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_session_entity_types), "__call__" + type(client.transport.list_session_entity_types), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( session_entity_type.ListSessionEntityTypesResponse() @@ -509,7 +602,7 @@ def test_list_session_entity_types_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_session_entity_types), "__call__" + type(client.transport.list_session_entity_types), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = session_entity_type.ListSessionEntityTypesResponse() @@ -545,7 +638,7 @@ async def test_list_session_entity_types_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_session_entity_types), "__call__" + type(client.transport.list_session_entity_types), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = session_entity_type.ListSessionEntityTypesResponse() @@ -584,7 +677,7 @@ def test_list_session_entity_types_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_session_entity_types), "__call__" + type(client.transport.list_session_entity_types), "__call__" ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -632,7 +725,7 @@ def test_list_session_entity_types_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_session_entity_types), "__call__" + type(client.transport.list_session_entity_types), "__call__" ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -672,7 +765,7 @@ async def test_list_session_entity_types_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_session_entity_types), + type(client.transport.list_session_entity_types), "__call__", new_callable=mock.AsyncMock, ) as call: @@ -721,7 +814,7 @@ async def test_list_session_entity_types_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_session_entity_types), + type(client.transport.list_session_entity_types), "__call__", new_callable=mock.AsyncMock, ) as call: @@ -771,7 +864,7 @@ def test_get_session_entity_type( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.get_session_entity_type), "__call__" + type(client.transport.get_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = session_entity_type.SessionEntityType( @@ -788,6 +881,7 @@ def test_get_session_entity_type( assert args[0] == session_entity_type.GetSessionEntityTypeRequest() # Establish that the response is the type that we expect. + assert isinstance(response, session_entity_type.SessionEntityType) assert response.name == "name_value" @@ -803,18 +897,21 @@ def test_get_session_entity_type_from_dict(): @pytest.mark.asyncio -async def test_get_session_entity_type_async(transport: str = "grpc_asyncio"): +async def test_get_session_entity_type_async( + transport: str = "grpc_asyncio", + request_type=session_entity_type.GetSessionEntityTypeRequest, +): client = SessionEntityTypesAsyncClient( 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 = session_entity_type.GetSessionEntityTypeRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.get_session_entity_type), "__call__" + type(client.transport.get_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -830,7 +927,7 @@ async def test_get_session_entity_type_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == session_entity_type.GetSessionEntityTypeRequest() # Establish that the response is the type that we expect. assert isinstance(response, session_entity_type.SessionEntityType) @@ -843,6 +940,11 @@ async def test_get_session_entity_type_async(transport: str = "grpc_asyncio"): ) +@pytest.mark.asyncio +async def test_get_session_entity_type_async_from_dict(): + await test_get_session_entity_type_async(request_type=dict) + + def test_get_session_entity_type_field_headers(): client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) @@ -853,7 +955,7 @@ def test_get_session_entity_type_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.get_session_entity_type), "__call__" + type(client.transport.get_session_entity_type), "__call__" ) as call: call.return_value = session_entity_type.SessionEntityType() @@ -882,7 +984,7 @@ async def test_get_session_entity_type_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.get_session_entity_type), "__call__" + type(client.transport.get_session_entity_type), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( session_entity_type.SessionEntityType() @@ -905,7 +1007,7 @@ def test_get_session_entity_type_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.get_session_entity_type), "__call__" + type(client.transport.get_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = session_entity_type.SessionEntityType() @@ -941,7 +1043,7 @@ async def test_get_session_entity_type_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.get_session_entity_type), "__call__" + type(client.transport.get_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = session_entity_type.SessionEntityType() @@ -989,7 +1091,7 @@ def test_create_session_entity_type( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_session_entity_type), "__call__" + type(client.transport.create_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_session_entity_type.SessionEntityType( @@ -1006,6 +1108,7 @@ def test_create_session_entity_type( assert args[0] == gcdc_session_entity_type.CreateSessionEntityTypeRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_session_entity_type.SessionEntityType) assert response.name == "name_value" @@ -1021,18 +1124,21 @@ def test_create_session_entity_type_from_dict(): @pytest.mark.asyncio -async def test_create_session_entity_type_async(transport: str = "grpc_asyncio"): +async def test_create_session_entity_type_async( + transport: str = "grpc_asyncio", + request_type=gcdc_session_entity_type.CreateSessionEntityTypeRequest, +): client = SessionEntityTypesAsyncClient( 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 = gcdc_session_entity_type.CreateSessionEntityTypeRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_session_entity_type), "__call__" + type(client.transport.create_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -1048,7 +1154,7 @@ async def test_create_session_entity_type_async(transport: str = "grpc_asyncio") assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_session_entity_type.CreateSessionEntityTypeRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_session_entity_type.SessionEntityType) @@ -1061,6 +1167,11 @@ async def test_create_session_entity_type_async(transport: str = "grpc_asyncio") ) +@pytest.mark.asyncio +async def test_create_session_entity_type_async_from_dict(): + await test_create_session_entity_type_async(request_type=dict) + + def test_create_session_entity_type_field_headers(): client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) @@ -1071,7 +1182,7 @@ def test_create_session_entity_type_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_session_entity_type), "__call__" + type(client.transport.create_session_entity_type), "__call__" ) as call: call.return_value = gcdc_session_entity_type.SessionEntityType() @@ -1100,7 +1211,7 @@ async def test_create_session_entity_type_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_session_entity_type), "__call__" + type(client.transport.create_session_entity_type), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_session_entity_type.SessionEntityType() @@ -1123,7 +1234,7 @@ def test_create_session_entity_type_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_session_entity_type), "__call__" + type(client.transport.create_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_session_entity_type.SessionEntityType() @@ -1174,7 +1285,7 @@ async def test_create_session_entity_type_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_session_entity_type), "__call__" + type(client.transport.create_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_session_entity_type.SessionEntityType() @@ -1237,7 +1348,7 @@ def test_update_session_entity_type( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_session_entity_type), "__call__" + type(client.transport.update_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_session_entity_type.SessionEntityType( @@ -1254,6 +1365,7 @@ def test_update_session_entity_type( assert args[0] == gcdc_session_entity_type.UpdateSessionEntityTypeRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_session_entity_type.SessionEntityType) assert response.name == "name_value" @@ -1269,18 +1381,21 @@ def test_update_session_entity_type_from_dict(): @pytest.mark.asyncio -async def test_update_session_entity_type_async(transport: str = "grpc_asyncio"): +async def test_update_session_entity_type_async( + transport: str = "grpc_asyncio", + request_type=gcdc_session_entity_type.UpdateSessionEntityTypeRequest, +): client = SessionEntityTypesAsyncClient( 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 = gcdc_session_entity_type.UpdateSessionEntityTypeRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_session_entity_type), "__call__" + type(client.transport.update_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -1296,7 +1411,7 @@ async def test_update_session_entity_type_async(transport: str = "grpc_asyncio") assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_session_entity_type.UpdateSessionEntityTypeRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_session_entity_type.SessionEntityType) @@ -1309,6 +1424,11 @@ async def test_update_session_entity_type_async(transport: str = "grpc_asyncio") ) +@pytest.mark.asyncio +async def test_update_session_entity_type_async_from_dict(): + await test_update_session_entity_type_async(request_type=dict) + + def test_update_session_entity_type_field_headers(): client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) @@ -1319,7 +1439,7 @@ def test_update_session_entity_type_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_session_entity_type), "__call__" + type(client.transport.update_session_entity_type), "__call__" ) as call: call.return_value = gcdc_session_entity_type.SessionEntityType() @@ -1351,7 +1471,7 @@ async def test_update_session_entity_type_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_session_entity_type), "__call__" + type(client.transport.update_session_entity_type), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_session_entity_type.SessionEntityType() @@ -1377,7 +1497,7 @@ def test_update_session_entity_type_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_session_entity_type), "__call__" + type(client.transport.update_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_session_entity_type.SessionEntityType() @@ -1428,7 +1548,7 @@ async def test_update_session_entity_type_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_session_entity_type), "__call__" + type(client.transport.update_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_session_entity_type.SessionEntityType() @@ -1491,7 +1611,7 @@ def test_delete_session_entity_type( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_session_entity_type), "__call__" + type(client.transport.delete_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1513,18 +1633,21 @@ def test_delete_session_entity_type_from_dict(): @pytest.mark.asyncio -async def test_delete_session_entity_type_async(transport: str = "grpc_asyncio"): +async def test_delete_session_entity_type_async( + transport: str = "grpc_asyncio", + request_type=session_entity_type.DeleteSessionEntityTypeRequest, +): client = SessionEntityTypesAsyncClient( 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 = session_entity_type.DeleteSessionEntityTypeRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_session_entity_type), "__call__" + type(client.transport.delete_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1535,12 +1658,17 @@ async def test_delete_session_entity_type_async(transport: str = "grpc_asyncio") assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == session_entity_type.DeleteSessionEntityTypeRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_delete_session_entity_type_async_from_dict(): + await test_delete_session_entity_type_async(request_type=dict) + + def test_delete_session_entity_type_field_headers(): client = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) @@ -1551,7 +1679,7 @@ def test_delete_session_entity_type_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_session_entity_type), "__call__" + type(client.transport.delete_session_entity_type), "__call__" ) as call: call.return_value = None @@ -1580,7 +1708,7 @@ async def test_delete_session_entity_type_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_session_entity_type), "__call__" + type(client.transport.delete_session_entity_type), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1601,7 +1729,7 @@ def test_delete_session_entity_type_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_session_entity_type), "__call__" + type(client.transport.delete_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1637,7 +1765,7 @@ async def test_delete_session_entity_type_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_session_entity_type), "__call__" + type(client.transport.delete_session_entity_type), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1705,7 +1833,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = SessionEntityTypesClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -1723,10 +1851,25 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [ + transports.SessionEntityTypesGrpcTransport, + transports.SessionEntityTypesGrpcAsyncIOTransport, + ], +) +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 = SessionEntityTypesClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.SessionEntityTypesGrpcTransport,) + assert isinstance(client.transport, transports.SessionEntityTypesGrpcTransport,) def test_session_entity_types_base_transport_error(): @@ -1784,6 +1927,17 @@ def test_session_entity_types_base_transport_with_credentials_file(): ) +def test_session_entity_types_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.dialogflowcx_v3beta1.services.session_entity_types.transports.SessionEntityTypesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.SessionEntityTypesTransport() + adc.assert_called_once() + + def test_session_entity_types_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -1822,7 +1976,7 @@ def test_session_entity_types_host_no_port(): api_endpoint="dialogflow.googleapis.com" ), ) - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_session_entity_types_host_with_port(): @@ -1832,197 +1986,125 @@ def test_session_entity_types_host_with_port(): api_endpoint="dialogflow.googleapis.com:8000" ), ) - assert client._transport._host == "dialogflow.googleapis.com:8000" + assert client.transport._host == "dialogflow.googleapis.com:8000" def test_session_entity_types_grpc_transport_channel(): channel = grpc.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.SessionEntityTypesGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_session_entity_types_grpc_asyncio_transport_channel(): channel = aio.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.SessionEntityTypesGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_session_entity_types_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.SessionEntityTypesGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_session_entity_types_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.SessionEntityTypesGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == None @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [ + transports.SessionEntityTypesGrpcTransport, + transports.SessionEntityTypesGrpcAsyncIOTransport, + ], ) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_session_entity_types_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint +def test_session_entity_types_transport_channel_mtls_with_client_cert_source( + transport_class, ): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.SessionEntityTypesGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [ + transports.SessionEntityTypesGrpcTransport, + transports.SessionEntityTypesGrpcAsyncIOTransport, + ], ) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_session_entity_types_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials class. +def test_session_entity_types_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), ): - mock_cred = mock.Mock() - transport = transports.SessionEntityTypesGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + with mock.patch.object( + transport_class, "create_channel", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel def test_session_entity_type_path(): @@ -2060,6 +2142,107 @@ def test_parse_session_entity_type_path(): assert expected == actual +def test_common_billing_account_path(): + billing_account = "scallop" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = SessionEntityTypesClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "abalone", + } + path = SessionEntityTypesClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = SessionEntityTypesClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "squid" + + expected = "folders/{folder}".format(folder=folder,) + actual = SessionEntityTypesClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "clam", + } + path = SessionEntityTypesClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = SessionEntityTypesClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "whelk" + + expected = "organizations/{organization}".format(organization=organization,) + actual = SessionEntityTypesClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "octopus", + } + path = SessionEntityTypesClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = SessionEntityTypesClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "oyster" + + expected = "projects/{project}".format(project=project,) + actual = SessionEntityTypesClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nudibranch", + } + path = SessionEntityTypesClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = SessionEntityTypesClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "cuttlefish" + location = "mussel" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = SessionEntityTypesClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "winkle", + "location": "nautilus", + } + path = SessionEntityTypesClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = SessionEntityTypesClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo() diff --git a/tests/unit/gapic/dialogflowcx_v3beta1/test_sessions.py b/tests/unit/gapic/dialogflowcx_v3beta1/test_sessions.py index e1e1a008..a8a4eb99 100644 --- a/tests/unit/gapic/dialogflowcx_v3beta1/test_sessions.py +++ b/tests/unit/gapic/dialogflowcx_v3beta1/test_sessions.py @@ -38,14 +38,9 @@ from google.cloud.dialogflowcx_v3beta1.types import audio_config from google.cloud.dialogflowcx_v3beta1.types import entity_type from google.cloud.dialogflowcx_v3beta1.types import intent -from google.cloud.dialogflowcx_v3beta1.types import intent as gcdc_intent from google.cloud.dialogflowcx_v3beta1.types import page from google.cloud.dialogflowcx_v3beta1.types import session -from google.cloud.dialogflowcx_v3beta1.types import session as gcdc_session from google.cloud.dialogflowcx_v3beta1.types import session_entity_type -from google.cloud.dialogflowcx_v3beta1.types import ( - session_entity_type as gcdc_session_entity_type, -) from google.oauth2 import service_account from google.protobuf import struct_pb2 as struct # type: ignore from google.type import latlng_pb2 as latlng # type: ignore @@ -98,12 +93,12 @@ def test_sessions_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_sessions_client_get_transport_class(): @@ -151,15 +146,14 @@ def test_sessions_client_client_options(client_class, transport_class, transport credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -168,15 +162,14 @@ def test_sessions_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -185,95 +178,173 @@ def test_sessions_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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, + ssl_channel_credentials=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", + [ + (SessionsClient, transports.SessionsGrpcTransport, "grpc", "true"), + ( + SessionsAsyncClient, + transports.SessionsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (SessionsClient, transports.SessionsGrpcTransport, "grpc", "false"), + ( + SessionsAsyncClient, + transports.SessionsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + SessionsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(SessionsClient) +) +@mock.patch.object( + SessionsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(SessionsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_sessions_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) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): - with mock.patch.object(transport_class, "__init__") as patched: + ssl_channel_creds = mock.Mock() with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds ): patched.return_value = None - client = client_class() + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=expected_ssl_channel_creds, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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=False, + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=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 has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - 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, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) @pytest.mark.parametrize( @@ -296,8 +367,7 @@ def test_sessions_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -323,8 +393,7 @@ def test_sessions_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -341,8 +410,7 @@ def test_sessions_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -360,7 +428,7 @@ def test_detect_intent( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.detect_intent), "__call__") as call: + with mock.patch.object(type(client.transport.detect_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = session.DetectIntentResponse( response_id="response_id_value", output_audio=b"output_audio_blob", @@ -375,6 +443,7 @@ def test_detect_intent( assert args[0] == session.DetectIntentRequest() # Establish that the response is the type that we expect. + assert isinstance(response, session.DetectIntentResponse) assert response.response_id == "response_id_value" @@ -387,19 +456,19 @@ def test_detect_intent_from_dict(): @pytest.mark.asyncio -async def test_detect_intent_async(transport: str = "grpc_asyncio"): +async def test_detect_intent_async( + transport: str = "grpc_asyncio", request_type=session.DetectIntentRequest +): client = SessionsAsyncClient( 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 = session.DetectIntentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.detect_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.detect_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( session.DetectIntentResponse( @@ -413,7 +482,7 @@ async def test_detect_intent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == session.DetectIntentRequest() # Establish that the response is the type that we expect. assert isinstance(response, session.DetectIntentResponse) @@ -423,6 +492,11 @@ async def test_detect_intent_async(transport: str = "grpc_asyncio"): assert response.output_audio == b"output_audio_blob" +@pytest.mark.asyncio +async def test_detect_intent_async_from_dict(): + await test_detect_intent_async(request_type=dict) + + def test_detect_intent_field_headers(): client = SessionsClient(credentials=credentials.AnonymousCredentials(),) @@ -432,7 +506,7 @@ def test_detect_intent_field_headers(): request.session = "session/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.detect_intent), "__call__") as call: + with mock.patch.object(type(client.transport.detect_intent), "__call__") as call: call.return_value = session.DetectIntentResponse() client.detect_intent(request) @@ -457,9 +531,7 @@ async def test_detect_intent_field_headers_async(): request.session = "session/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.detect_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.detect_intent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( session.DetectIntentResponse() ) @@ -491,7 +563,7 @@ def test_streaming_detect_intent( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.streaming_detect_intent), "__call__" + type(client.transport.streaming_detect_intent), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = iter([session.StreamingDetectIntentResponse()]) @@ -514,20 +586,22 @@ def test_streaming_detect_intent_from_dict(): @pytest.mark.asyncio -async def test_streaming_detect_intent_async(transport: str = "grpc_asyncio"): +async def test_streaming_detect_intent_async( + transport: str = "grpc_asyncio", request_type=session.StreamingDetectIntentRequest +): client = SessionsAsyncClient( 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 = session.StreamingDetectIntentRequest() + request = request_type() requests = [request] # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.streaming_detect_intent), "__call__" + type(client.transport.streaming_detect_intent), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = mock.Mock(aio.StreamStreamCall, autospec=True) @@ -548,6 +622,11 @@ async def test_streaming_detect_intent_async(transport: str = "grpc_asyncio"): assert isinstance(message, session.StreamingDetectIntentResponse) +@pytest.mark.asyncio +async def test_streaming_detect_intent_async_from_dict(): + await test_streaming_detect_intent_async(request_type=dict) + + def test_match_intent(transport: str = "grpc", request_type=session.MatchIntentRequest): client = SessionsClient( credentials=credentials.AnonymousCredentials(), transport=transport, @@ -558,7 +637,7 @@ def test_match_intent(transport: str = "grpc", request_type=session.MatchIntentR request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.match_intent), "__call__") as call: + with mock.patch.object(type(client.transport.match_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = session.MatchIntentResponse(text="text_value",) @@ -571,6 +650,7 @@ def test_match_intent(transport: str = "grpc", request_type=session.MatchIntentR assert args[0] == session.MatchIntentRequest() # Establish that the response is the type that we expect. + assert isinstance(response, session.MatchIntentResponse) @@ -579,19 +659,19 @@ def test_match_intent_from_dict(): @pytest.mark.asyncio -async def test_match_intent_async(transport: str = "grpc_asyncio"): +async def test_match_intent_async( + transport: str = "grpc_asyncio", request_type=session.MatchIntentRequest +): client = SessionsAsyncClient( 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 = session.MatchIntentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.match_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.match_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( session.MatchIntentResponse() @@ -603,12 +683,17 @@ async def test_match_intent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == session.MatchIntentRequest() # Establish that the response is the type that we expect. assert isinstance(response, session.MatchIntentResponse) +@pytest.mark.asyncio +async def test_match_intent_async_from_dict(): + await test_match_intent_async(request_type=dict) + + def test_match_intent_field_headers(): client = SessionsClient(credentials=credentials.AnonymousCredentials(),) @@ -618,7 +703,7 @@ def test_match_intent_field_headers(): request.session = "session/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.match_intent), "__call__") as call: + with mock.patch.object(type(client.transport.match_intent), "__call__") as call: call.return_value = session.MatchIntentResponse() client.match_intent(request) @@ -643,9 +728,7 @@ async def test_match_intent_field_headers_async(): request.session = "session/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.match_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.match_intent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( session.MatchIntentResponse() ) @@ -674,7 +757,7 @@ def test_fulfill_intent( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.fulfill_intent), "__call__") as call: + with mock.patch.object(type(client.transport.fulfill_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = session.FulfillIntentResponse( response_id="response_id_value", output_audio=b"output_audio_blob", @@ -689,6 +772,7 @@ def test_fulfill_intent( assert args[0] == session.FulfillIntentRequest() # Establish that the response is the type that we expect. + assert isinstance(response, session.FulfillIntentResponse) assert response.response_id == "response_id_value" @@ -701,19 +785,19 @@ def test_fulfill_intent_from_dict(): @pytest.mark.asyncio -async def test_fulfill_intent_async(transport: str = "grpc_asyncio"): +async def test_fulfill_intent_async( + transport: str = "grpc_asyncio", request_type=session.FulfillIntentRequest +): client = SessionsAsyncClient( 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 = session.FulfillIntentRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.fulfill_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.fulfill_intent), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( session.FulfillIntentResponse( @@ -727,7 +811,7 @@ async def test_fulfill_intent_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == session.FulfillIntentRequest() # Establish that the response is the type that we expect. assert isinstance(response, session.FulfillIntentResponse) @@ -737,6 +821,11 @@ async def test_fulfill_intent_async(transport: str = "grpc_asyncio"): assert response.output_audio == b"output_audio_blob" +@pytest.mark.asyncio +async def test_fulfill_intent_async_from_dict(): + await test_fulfill_intent_async(request_type=dict) + + def test_fulfill_intent_field_headers(): client = SessionsClient(credentials=credentials.AnonymousCredentials(),) @@ -746,7 +835,7 @@ def test_fulfill_intent_field_headers(): request.match_intent_request.session = "match_intent_request.session/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.fulfill_intent), "__call__") as call: + with mock.patch.object(type(client.transport.fulfill_intent), "__call__") as call: call.return_value = session.FulfillIntentResponse() client.fulfill_intent(request) @@ -774,9 +863,7 @@ async def test_fulfill_intent_field_headers_async(): request.match_intent_request.session = "match_intent_request.session/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.fulfill_intent), "__call__" - ) as call: + with mock.patch.object(type(client.transport.fulfill_intent), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( session.FulfillIntentResponse() ) @@ -832,7 +919,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = SessionsClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -850,10 +937,22 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], +) +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 = SessionsClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.SessionsGrpcTransport,) + assert isinstance(client.transport, transports.SessionsGrpcTransport,) def test_sessions_base_transport_error(): @@ -910,6 +1009,17 @@ def test_sessions_base_transport_with_credentials_file(): ) +def test_sessions_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.dialogflowcx_v3beta1.services.sessions.transports.SessionsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.SessionsTransport() + adc.assert_called_once() + + def test_sessions_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -948,7 +1058,7 @@ def test_sessions_host_no_port(): api_endpoint="dialogflow.googleapis.com" ), ) - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_sessions_host_with_port(): @@ -958,205 +1068,262 @@ def test_sessions_host_with_port(): api_endpoint="dialogflow.googleapis.com:8000" ), ) - assert client._transport._host == "dialogflow.googleapis.com:8000" + assert client.transport._host == "dialogflow.googleapis.com:8000" def test_sessions_grpc_transport_channel(): channel = grpc.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.SessionsGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_sessions_grpc_asyncio_transport_channel(): channel = aio.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.SessionsGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_sessions_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() +@pytest.mark.parametrize( + "transport_class", + [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], +) +def test_sessions_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + +@pytest.mark.parametrize( + "transport_class", + [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], +) +def test_sessions_transport_channel_mtls_with_adc(transport_class): mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred + 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", autospec=True + ) 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, + ) - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel + 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel - transport = transports.SessionsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, + +def test_entity_type_path(): + project = "squid" + location = "clam" + agent = "whelk" + entity_type = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/entityTypes/{entity_type}".format( + project=project, location=location, agent=agent, entity_type=entity_type, ) - assert transport.grpc_channel == mock_grpc_channel + actual = SessionsClient.entity_type_path(project, location, agent, entity_type) + assert expected == actual -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_sessions_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() +def test_parse_entity_type_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "entity_type": "mussel", + } + path = SessionsClient.entity_type_path(**expected) - mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred + # Check that the path construction is reversible. + actual = SessionsClient.parse_entity_type_path(path) + assert expected == actual - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - transport = transports.SessionsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, +def test_flow_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + flow = "abalone" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, ) - assert transport.grpc_channel == mock_grpc_channel + actual = SessionsClient.flow_path(project, location, agent, flow) + assert expected == actual -@pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] -) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_sessions_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel +def test_parse_flow_path(): + expected = { + "project": "squid", + "location": "clam", + "agent": "whelk", + "flow": "octopus", + } + path = SessionsClient.flow_path(**expected) - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.SessionsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + # Check that the path construction is reversible. + actual = SessionsClient.parse_flow_path(path) + assert expected == actual -@pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] -) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_sessions_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel +def test_intent_path(): + project = "oyster" + location = "nudibranch" + agent = "cuttlefish" + intent = "mussel" - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.SessionsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + expected = "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, + ) + actual = SessionsClient.intent_path(project, location, agent, intent) + assert expected == actual -def test_session_entity_type_path(): +def test_parse_intent_path(): + expected = { + "project": "winkle", + "location": "nautilus", + "agent": "scallop", + "intent": "abalone", + } + path = SessionsClient.intent_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_intent_path(path) + assert expected == actual + + +def test_page_path(): project = "squid" location = "clam" agent = "whelk" - session = "octopus" - entity_type = "oyster" + flow = "octopus" + page = "oyster" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + actual = SessionsClient.page_path(project, location, agent, flow, page) + assert expected == actual + + +def test_parse_page_path(): + expected = { + "project": "nudibranch", + "location": "cuttlefish", + "agent": "mussel", + "flow": "winkle", + "page": "nautilus", + } + path = SessionsClient.page_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_page_path(path) + assert expected == actual + + +def test_session_path(): + project = "scallop" + location = "abalone" + agent = "squid" + session = "clam" + + expected = "projects/{project}/locations/{location}/agents/{agent}/sessions/{session}".format( + project=project, location=location, agent=agent, session=session, + ) + actual = SessionsClient.session_path(project, location, agent, session) + assert expected == actual + + +def test_parse_session_path(): + expected = { + "project": "whelk", + "location": "octopus", + "agent": "oyster", + "session": "nudibranch", + } + path = SessionsClient.session_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_session_path(path) + assert expected == actual + + +def test_session_entity_type_path(): + project = "cuttlefish" + location = "mussel" + agent = "winkle" + session = "nautilus" + entity_type = "scallop" expected = "projects/{project}/locations/{location}/agents/{agent}/sessions/{session}/entityTypes/{entity_type}".format( project=project, @@ -1173,11 +1340,11 @@ def test_session_entity_type_path(): def test_parse_session_entity_type_path(): expected = { - "project": "nudibranch", - "location": "cuttlefish", - "agent": "mussel", - "session": "winkle", - "entity_type": "nautilus", + "project": "abalone", + "location": "squid", + "agent": "clam", + "session": "whelk", + "entity_type": "octopus", } path = SessionsClient.session_entity_type_path(**expected) @@ -1186,30 +1353,166 @@ def test_parse_session_entity_type_path(): assert expected == actual -def test_intent_path(): - project = "squid" - location = "clam" - agent = "whelk" - intent = "octopus" +def test_transition_route_group_path(): + project = "oyster" + location = "nudibranch" + agent = "cuttlefish" + flow = "mussel" + transition_route_group = "winkle" - expected = "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( - project=project, location=location, agent=agent, intent=intent, + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/transitionRouteGroups/{transition_route_group}".format( + project=project, + location=location, + agent=agent, + flow=flow, + transition_route_group=transition_route_group, + ) + actual = SessionsClient.transition_route_group_path( + project, location, agent, flow, transition_route_group ) - actual = SessionsClient.intent_path(project, location, agent, intent) assert expected == actual -def test_parse_intent_path(): +def test_parse_transition_route_group_path(): expected = { - "project": "oyster", - "location": "nudibranch", - "agent": "cuttlefish", - "intent": "mussel", + "project": "nautilus", + "location": "scallop", + "agent": "abalone", + "flow": "squid", + "transition_route_group": "clam", } - path = SessionsClient.intent_path(**expected) + path = SessionsClient.transition_route_group_path(**expected) # Check that the path construction is reversible. - actual = SessionsClient.parse_intent_path(path) + actual = SessionsClient.parse_transition_route_group_path(path) + assert expected == actual + + +def test_webhook_path(): + project = "whelk" + location = "octopus" + agent = "oyster" + webhook = "nudibranch" + + expected = "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + actual = SessionsClient.webhook_path(project, location, agent, webhook) + assert expected == actual + + +def test_parse_webhook_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + "agent": "winkle", + "webhook": "nautilus", + } + path = SessionsClient.webhook_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_webhook_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "scallop" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = SessionsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "abalone", + } + path = SessionsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "squid" + + expected = "folders/{folder}".format(folder=folder,) + actual = SessionsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "clam", + } + path = SessionsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "whelk" + + expected = "organizations/{organization}".format(organization=organization,) + actual = SessionsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "octopus", + } + path = SessionsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "oyster" + + expected = "projects/{project}".format(project=project,) + actual = SessionsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nudibranch", + } + path = SessionsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "cuttlefish" + location = "mussel" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = SessionsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "winkle", + "location": "nautilus", + } + path = SessionsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = SessionsClient.parse_common_location_path(path) assert expected == actual diff --git a/tests/unit/gapic/dialogflowcx_v3beta1/test_transition_route_groups.py b/tests/unit/gapic/dialogflowcx_v3beta1/test_transition_route_groups.py index 2ecd8fc8..5dd04fb9 100644 --- a/tests/unit/gapic/dialogflowcx_v3beta1/test_transition_route_groups.py +++ b/tests/unit/gapic/dialogflowcx_v3beta1/test_transition_route_groups.py @@ -44,7 +44,6 @@ ) from google.cloud.dialogflowcx_v3beta1.types import fulfillment from google.cloud.dialogflowcx_v3beta1.types import page -from google.cloud.dialogflowcx_v3beta1.types import page as gcdc_page from google.cloud.dialogflowcx_v3beta1.types import response_message from google.cloud.dialogflowcx_v3beta1.types import transition_route_group from google.cloud.dialogflowcx_v3beta1.types import ( @@ -110,12 +109,12 @@ def test_transition_route_groups_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_transition_route_groups_client_get_transport_class(): @@ -175,15 +174,14 @@ def test_transition_route_groups_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -192,15 +190,14 @@ def test_transition_route_groups_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -209,95 +206,185 @@ def test_transition_route_groups_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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, + ssl_channel_credentials=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", + [ + ( + TransitionRouteGroupsClient, + transports.TransitionRouteGroupsGrpcTransport, + "grpc", + "true", + ), + ( + TransitionRouteGroupsAsyncClient, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + ( + TransitionRouteGroupsClient, + transports.TransitionRouteGroupsGrpcTransport, + "grpc", + "false", + ), + ( + TransitionRouteGroupsAsyncClient, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + TransitionRouteGroupsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TransitionRouteGroupsClient), +) +@mock.patch.object( + TransitionRouteGroupsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(TransitionRouteGroupsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_transition_route_groups_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) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): - with mock.patch.object(transport_class, "__init__") as patched: + ssl_channel_creds = mock.Mock() with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds ): patched.return_value = None - client = client_class() + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=expected_ssl_channel_creds, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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=False, + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=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 has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - 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, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) @pytest.mark.parametrize( @@ -328,8 +415,7 @@ def test_transition_route_groups_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -363,8 +449,7 @@ def test_transition_route_groups_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -383,8 +468,7 @@ def test_transition_route_groups_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -404,7 +488,7 @@ def test_list_transition_route_groups( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_transition_route_groups), "__call__" + type(client.transport.list_transition_route_groups), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = transition_route_group.ListTransitionRouteGroupsResponse( @@ -420,6 +504,7 @@ def test_list_transition_route_groups( assert args[0] == transition_route_group.ListTransitionRouteGroupsRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListTransitionRouteGroupsPager) assert response.next_page_token == "next_page_token_value" @@ -430,18 +515,21 @@ def test_list_transition_route_groups_from_dict(): @pytest.mark.asyncio -async def test_list_transition_route_groups_async(transport: str = "grpc_asyncio"): +async def test_list_transition_route_groups_async( + transport: str = "grpc_asyncio", + request_type=transition_route_group.ListTransitionRouteGroupsRequest, +): client = TransitionRouteGroupsAsyncClient( 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 = transition_route_group.ListTransitionRouteGroupsRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_transition_route_groups), "__call__" + type(client.transport.list_transition_route_groups), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -456,7 +544,7 @@ async def test_list_transition_route_groups_async(transport: str = "grpc_asyncio assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == transition_route_group.ListTransitionRouteGroupsRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListTransitionRouteGroupsAsyncPager) @@ -464,6 +552,11 @@ async def test_list_transition_route_groups_async(transport: str = "grpc_asyncio assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_list_transition_route_groups_async_from_dict(): + await test_list_transition_route_groups_async(request_type=dict) + + def test_list_transition_route_groups_field_headers(): client = TransitionRouteGroupsClient( credentials=credentials.AnonymousCredentials(), @@ -476,7 +569,7 @@ def test_list_transition_route_groups_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_transition_route_groups), "__call__" + type(client.transport.list_transition_route_groups), "__call__" ) as call: call.return_value = transition_route_group.ListTransitionRouteGroupsResponse() @@ -505,7 +598,7 @@ async def test_list_transition_route_groups_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_transition_route_groups), "__call__" + type(client.transport.list_transition_route_groups), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( transition_route_group.ListTransitionRouteGroupsResponse() @@ -530,7 +623,7 @@ def test_list_transition_route_groups_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_transition_route_groups), "__call__" + type(client.transport.list_transition_route_groups), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = transition_route_group.ListTransitionRouteGroupsResponse() @@ -569,7 +662,7 @@ async def test_list_transition_route_groups_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_transition_route_groups), "__call__" + type(client.transport.list_transition_route_groups), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = transition_route_group.ListTransitionRouteGroupsResponse() @@ -609,7 +702,7 @@ def test_list_transition_route_groups_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_transition_route_groups), "__call__" + type(client.transport.list_transition_route_groups), "__call__" ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -659,7 +752,7 @@ def test_list_transition_route_groups_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.list_transition_route_groups), "__call__" + type(client.transport.list_transition_route_groups), "__call__" ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -701,7 +794,7 @@ async def test_list_transition_route_groups_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_transition_route_groups), + type(client.transport.list_transition_route_groups), "__call__", new_callable=mock.AsyncMock, ) as call: @@ -753,7 +846,7 @@ async def test_list_transition_route_groups_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_transition_route_groups), + type(client.transport.list_transition_route_groups), "__call__", new_callable=mock.AsyncMock, ) as call: @@ -807,7 +900,7 @@ def test_get_transition_route_group( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.get_transition_route_group), "__call__" + type(client.transport.get_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = transition_route_group.TransitionRouteGroup( @@ -823,6 +916,7 @@ def test_get_transition_route_group( assert args[0] == transition_route_group.GetTransitionRouteGroupRequest() # Establish that the response is the type that we expect. + assert isinstance(response, transition_route_group.TransitionRouteGroup) assert response.name == "name_value" @@ -835,18 +929,21 @@ def test_get_transition_route_group_from_dict(): @pytest.mark.asyncio -async def test_get_transition_route_group_async(transport: str = "grpc_asyncio"): +async def test_get_transition_route_group_async( + transport: str = "grpc_asyncio", + request_type=transition_route_group.GetTransitionRouteGroupRequest, +): client = TransitionRouteGroupsAsyncClient( 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 = transition_route_group.GetTransitionRouteGroupRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.get_transition_route_group), "__call__" + type(client.transport.get_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -861,7 +958,7 @@ async def test_get_transition_route_group_async(transport: str = "grpc_asyncio") assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == transition_route_group.GetTransitionRouteGroupRequest() # Establish that the response is the type that we expect. assert isinstance(response, transition_route_group.TransitionRouteGroup) @@ -871,6 +968,11 @@ async def test_get_transition_route_group_async(transport: str = "grpc_asyncio") assert response.display_name == "display_name_value" +@pytest.mark.asyncio +async def test_get_transition_route_group_async_from_dict(): + await test_get_transition_route_group_async(request_type=dict) + + def test_get_transition_route_group_field_headers(): client = TransitionRouteGroupsClient( credentials=credentials.AnonymousCredentials(), @@ -883,7 +985,7 @@ def test_get_transition_route_group_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.get_transition_route_group), "__call__" + type(client.transport.get_transition_route_group), "__call__" ) as call: call.return_value = transition_route_group.TransitionRouteGroup() @@ -912,7 +1014,7 @@ async def test_get_transition_route_group_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.get_transition_route_group), "__call__" + type(client.transport.get_transition_route_group), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( transition_route_group.TransitionRouteGroup() @@ -937,7 +1039,7 @@ def test_get_transition_route_group_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.get_transition_route_group), "__call__" + type(client.transport.get_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = transition_route_group.TransitionRouteGroup() @@ -975,7 +1077,7 @@ async def test_get_transition_route_group_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.get_transition_route_group), "__call__" + type(client.transport.get_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = transition_route_group.TransitionRouteGroup() @@ -1023,7 +1125,7 @@ def test_create_transition_route_group( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_transition_route_group), "__call__" + type(client.transport.create_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_transition_route_group.TransitionRouteGroup( @@ -1041,6 +1143,7 @@ def test_create_transition_route_group( ) # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_transition_route_group.TransitionRouteGroup) assert response.name == "name_value" @@ -1053,18 +1156,21 @@ def test_create_transition_route_group_from_dict(): @pytest.mark.asyncio -async def test_create_transition_route_group_async(transport: str = "grpc_asyncio"): +async def test_create_transition_route_group_async( + transport: str = "grpc_asyncio", + request_type=gcdc_transition_route_group.CreateTransitionRouteGroupRequest, +): client = TransitionRouteGroupsAsyncClient( 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 = gcdc_transition_route_group.CreateTransitionRouteGroupRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_transition_route_group), "__call__" + type(client.transport.create_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -1079,7 +1185,9 @@ async def test_create_transition_route_group_async(transport: str = "grpc_asynci assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert ( + args[0] == gcdc_transition_route_group.CreateTransitionRouteGroupRequest() + ) # Establish that the response is the type that we expect. assert isinstance(response, gcdc_transition_route_group.TransitionRouteGroup) @@ -1089,6 +1197,11 @@ async def test_create_transition_route_group_async(transport: str = "grpc_asynci assert response.display_name == "display_name_value" +@pytest.mark.asyncio +async def test_create_transition_route_group_async_from_dict(): + await test_create_transition_route_group_async(request_type=dict) + + def test_create_transition_route_group_field_headers(): client = TransitionRouteGroupsClient( credentials=credentials.AnonymousCredentials(), @@ -1101,7 +1214,7 @@ def test_create_transition_route_group_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_transition_route_group), "__call__" + type(client.transport.create_transition_route_group), "__call__" ) as call: call.return_value = gcdc_transition_route_group.TransitionRouteGroup() @@ -1130,7 +1243,7 @@ async def test_create_transition_route_group_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_transition_route_group), "__call__" + type(client.transport.create_transition_route_group), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_transition_route_group.TransitionRouteGroup() @@ -1155,7 +1268,7 @@ def test_create_transition_route_group_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.create_transition_route_group), "__call__" + type(client.transport.create_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_transition_route_group.TransitionRouteGroup() @@ -1208,7 +1321,7 @@ async def test_create_transition_route_group_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.create_transition_route_group), "__call__" + type(client.transport.create_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_transition_route_group.TransitionRouteGroup() @@ -1271,7 +1384,7 @@ def test_update_transition_route_group( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_transition_route_group), "__call__" + type(client.transport.update_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_transition_route_group.TransitionRouteGroup( @@ -1289,6 +1402,7 @@ def test_update_transition_route_group( ) # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_transition_route_group.TransitionRouteGroup) assert response.name == "name_value" @@ -1301,18 +1415,21 @@ def test_update_transition_route_group_from_dict(): @pytest.mark.asyncio -async def test_update_transition_route_group_async(transport: str = "grpc_asyncio"): +async def test_update_transition_route_group_async( + transport: str = "grpc_asyncio", + request_type=gcdc_transition_route_group.UpdateTransitionRouteGroupRequest, +): client = TransitionRouteGroupsAsyncClient( 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 = gcdc_transition_route_group.UpdateTransitionRouteGroupRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_transition_route_group), "__call__" + type(client.transport.update_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( @@ -1327,7 +1444,9 @@ async def test_update_transition_route_group_async(transport: str = "grpc_asynci assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert ( + args[0] == gcdc_transition_route_group.UpdateTransitionRouteGroupRequest() + ) # Establish that the response is the type that we expect. assert isinstance(response, gcdc_transition_route_group.TransitionRouteGroup) @@ -1337,6 +1456,11 @@ async def test_update_transition_route_group_async(transport: str = "grpc_asynci assert response.display_name == "display_name_value" +@pytest.mark.asyncio +async def test_update_transition_route_group_async_from_dict(): + await test_update_transition_route_group_async(request_type=dict) + + def test_update_transition_route_group_field_headers(): client = TransitionRouteGroupsClient( credentials=credentials.AnonymousCredentials(), @@ -1349,7 +1473,7 @@ def test_update_transition_route_group_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_transition_route_group), "__call__" + type(client.transport.update_transition_route_group), "__call__" ) as call: call.return_value = gcdc_transition_route_group.TransitionRouteGroup() @@ -1381,7 +1505,7 @@ async def test_update_transition_route_group_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_transition_route_group), "__call__" + type(client.transport.update_transition_route_group), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_transition_route_group.TransitionRouteGroup() @@ -1409,7 +1533,7 @@ def test_update_transition_route_group_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.update_transition_route_group), "__call__" + type(client.transport.update_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_transition_route_group.TransitionRouteGroup() @@ -1462,7 +1586,7 @@ async def test_update_transition_route_group_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.update_transition_route_group), "__call__" + type(client.transport.update_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = gcdc_transition_route_group.TransitionRouteGroup() @@ -1525,7 +1649,7 @@ def test_delete_transition_route_group( # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_transition_route_group), "__call__" + type(client.transport.delete_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1547,18 +1671,21 @@ def test_delete_transition_route_group_from_dict(): @pytest.mark.asyncio -async def test_delete_transition_route_group_async(transport: str = "grpc_asyncio"): +async def test_delete_transition_route_group_async( + transport: str = "grpc_asyncio", + request_type=transition_route_group.DeleteTransitionRouteGroupRequest, +): client = TransitionRouteGroupsAsyncClient( 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 = transition_route_group.DeleteTransitionRouteGroupRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_transition_route_group), "__call__" + type(client.transport.delete_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1569,12 +1696,17 @@ async def test_delete_transition_route_group_async(transport: str = "grpc_asynci assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == transition_route_group.DeleteTransitionRouteGroupRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_delete_transition_route_group_async_from_dict(): + await test_delete_transition_route_group_async(request_type=dict) + + def test_delete_transition_route_group_field_headers(): client = TransitionRouteGroupsClient( credentials=credentials.AnonymousCredentials(), @@ -1587,7 +1719,7 @@ def test_delete_transition_route_group_field_headers(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_transition_route_group), "__call__" + type(client.transport.delete_transition_route_group), "__call__" ) as call: call.return_value = None @@ -1616,7 +1748,7 @@ async def test_delete_transition_route_group_field_headers_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_transition_route_group), "__call__" + type(client.transport.delete_transition_route_group), "__call__" ) as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1639,7 +1771,7 @@ def test_delete_transition_route_group_flattened(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._transport.delete_transition_route_group), "__call__" + type(client.transport.delete_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1678,7 +1810,7 @@ async def test_delete_transition_route_group_flattened_async(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.delete_transition_route_group), "__call__" + type(client.transport.delete_transition_route_group), "__call__" ) as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1747,7 +1879,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = TransitionRouteGroupsClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -1765,12 +1897,27 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [ + transports.TransitionRouteGroupsGrpcTransport, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + ], +) +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 = TransitionRouteGroupsClient( credentials=credentials.AnonymousCredentials(), ) - assert isinstance(client._transport, transports.TransitionRouteGroupsGrpcTransport,) + assert isinstance(client.transport, transports.TransitionRouteGroupsGrpcTransport,) def test_transition_route_groups_base_transport_error(): @@ -1828,6 +1975,17 @@ def test_transition_route_groups_base_transport_with_credentials_file(): ) +def test_transition_route_groups_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.dialogflowcx_v3beta1.services.transition_route_groups.transports.TransitionRouteGroupsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.TransitionRouteGroupsTransport() + adc.assert_called_once() + + def test_transition_route_groups_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -1866,7 +2024,7 @@ def test_transition_route_groups_host_no_port(): api_endpoint="dialogflow.googleapis.com" ), ) - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_transition_route_groups_host_with_port(): @@ -1876,205 +2034,216 @@ def test_transition_route_groups_host_with_port(): api_endpoint="dialogflow.googleapis.com:8000" ), ) - assert client._transport._host == "dialogflow.googleapis.com:8000" + assert client.transport._host == "dialogflow.googleapis.com:8000" def test_transition_route_groups_grpc_transport_channel(): channel = grpc.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.TransitionRouteGroupsGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_transition_route_groups_grpc_asyncio_transport_channel(): channel = aio.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.TransitionRouteGroupsGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_transition_route_groups_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred +@pytest.mark.parametrize( + "transport_class", + [ + transports.TransitionRouteGroupsGrpcTransport, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + ], +) +def test_transition_route_groups_transport_channel_mtls_with_client_cert_source( + transport_class, ): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + +@pytest.mark.parametrize( + "transport_class", + [ + transports.TransitionRouteGroupsGrpcTransport, + transports.TransitionRouteGroupsGrpcAsyncIOTransport, + ], +) +def test_transition_route_groups_transport_channel_mtls_with_adc(transport_class): mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred + 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", autospec=True + ) 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, + ) - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel + 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel - transport = transports.TransitionRouteGroupsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, + +def test_flow_path(): + project = "squid" + location = "clam" + agent = "whelk" + flow = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}".format( + project=project, location=location, agent=agent, flow=flow, ) - assert transport.grpc_channel == mock_grpc_channel + actual = TransitionRouteGroupsClient.flow_path(project, location, agent, flow) + assert expected == actual -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_transition_route_groups_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() +def test_parse_flow_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "flow": "mussel", + } + path = TransitionRouteGroupsClient.flow_path(**expected) - mock_ssl_cred = mock.Mock() - grpc_ssl_channel_cred.return_value = mock_ssl_cred + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_flow_path(path) + assert expected == actual - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - transport = transports.TransitionRouteGroupsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, +def test_intent_path(): + project = "winkle" + location = "nautilus" + agent = "scallop" + intent = "abalone" + + expected = "projects/{project}/locations/{location}/agents/{agent}/intents/{intent}".format( + project=project, location=location, agent=agent, intent=intent, ) - assert transport.grpc_channel == mock_grpc_channel + actual = TransitionRouteGroupsClient.intent_path(project, location, agent, intent) + assert expected == actual -@pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] -) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_transition_route_groups_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel +def test_parse_intent_path(): + expected = { + "project": "squid", + "location": "clam", + "agent": "whelk", + "intent": "octopus", + } + path = TransitionRouteGroupsClient.intent_path(**expected) - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.TransitionRouteGroupsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_intent_path(path) + assert expected == actual -@pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] -) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_transition_route_groups_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel +def test_page_path(): + project = "oyster" + location = "nudibranch" + agent = "cuttlefish" + flow = "mussel" + page = "winkle" + + expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/pages/{page}".format( + project=project, location=location, agent=agent, flow=flow, page=page, + ) + actual = TransitionRouteGroupsClient.page_path(project, location, agent, flow, page) + assert expected == actual - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.TransitionRouteGroupsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + +def test_parse_page_path(): + expected = { + "project": "nautilus", + "location": "scallop", + "agent": "abalone", + "flow": "squid", + "page": "clam", + } + path = TransitionRouteGroupsClient.page_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_page_path(path) + assert expected == actual def test_transition_route_group_path(): - project = "squid" - location = "clam" - agent = "whelk" - flow = "octopus" - transition_route_group = "oyster" + project = "whelk" + location = "octopus" + agent = "oyster" + flow = "nudibranch" + transition_route_group = "cuttlefish" expected = "projects/{project}/locations/{location}/agents/{agent}/flows/{flow}/transitionRouteGroups/{transition_route_group}".format( project=project, @@ -2091,11 +2260,11 @@ def test_transition_route_group_path(): def test_parse_transition_route_group_path(): expected = { - "project": "nudibranch", - "location": "cuttlefish", - "agent": "mussel", - "flow": "winkle", - "transition_route_group": "nautilus", + "project": "mussel", + "location": "winkle", + "agent": "nautilus", + "flow": "scallop", + "transition_route_group": "abalone", } path = TransitionRouteGroupsClient.transition_route_group_path(**expected) @@ -2104,6 +2273,134 @@ def test_parse_transition_route_group_path(): assert expected == actual +def test_webhook_path(): + project = "squid" + location = "clam" + agent = "whelk" + webhook = "octopus" + + expected = "projects/{project}/locations/{location}/agents/{agent}/webhooks/{webhook}".format( + project=project, location=location, agent=agent, webhook=webhook, + ) + actual = TransitionRouteGroupsClient.webhook_path(project, location, agent, webhook) + assert expected == actual + + +def test_parse_webhook_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + "agent": "cuttlefish", + "webhook": "mussel", + } + path = TransitionRouteGroupsClient.webhook_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_webhook_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "winkle" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = TransitionRouteGroupsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nautilus", + } + path = TransitionRouteGroupsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "scallop" + + expected = "folders/{folder}".format(folder=folder,) + actual = TransitionRouteGroupsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "abalone", + } + path = TransitionRouteGroupsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "squid" + + expected = "organizations/{organization}".format(organization=organization,) + actual = TransitionRouteGroupsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "clam", + } + path = TransitionRouteGroupsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "whelk" + + expected = "projects/{project}".format(project=project,) + actual = TransitionRouteGroupsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "octopus", + } + path = TransitionRouteGroupsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.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 = TransitionRouteGroupsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + } + path = TransitionRouteGroupsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = TransitionRouteGroupsClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo() diff --git a/tests/unit/gapic/dialogflowcx_v3beta1/test_versions.py b/tests/unit/gapic/dialogflowcx_v3beta1/test_versions.py index 2d2123d6..13d7b5f8 100644 --- a/tests/unit/gapic/dialogflowcx_v3beta1/test_versions.py +++ b/tests/unit/gapic/dialogflowcx_v3beta1/test_versions.py @@ -31,7 +31,7 @@ from google.api_core import gapic_v1 from google.api_core import grpc_helpers from google.api_core import grpc_helpers_async -from google.api_core import operation_async +from google.api_core import operation_async # type: ignore from google.api_core import operations_v1 from google.auth import credentials from google.auth.exceptions import MutualTLSChannelError @@ -40,7 +40,6 @@ from google.cloud.dialogflowcx_v3beta1.services.versions import pagers from google.cloud.dialogflowcx_v3beta1.services.versions import transports from google.cloud.dialogflowcx_v3beta1.types import flow -from google.cloud.dialogflowcx_v3beta1.types import flow as gcdc_flow from google.cloud.dialogflowcx_v3beta1.types import version from google.cloud.dialogflowcx_v3beta1.types import version as gcdc_version from google.longrunning import operations_pb2 @@ -97,12 +96,12 @@ def test_versions_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_versions_client_get_transport_class(): @@ -150,15 +149,14 @@ def test_versions_client_client_options(client_class, transport_class, transport credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -167,15 +165,14 @@ def test_versions_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -184,95 +181,173 @@ def test_versions_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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, + ssl_channel_credentials=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", + [ + (VersionsClient, transports.VersionsGrpcTransport, "grpc", "true"), + ( + VersionsAsyncClient, + transports.VersionsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (VersionsClient, transports.VersionsGrpcTransport, "grpc", "false"), + ( + VersionsAsyncClient, + transports.VersionsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + VersionsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(VersionsClient) +) +@mock.patch.object( + VersionsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(VersionsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_versions_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) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): - with mock.patch.object(transport_class, "__init__") as patched: + ssl_channel_creds = mock.Mock() with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds ): patched.return_value = None - client = client_class() + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=expected_ssl_channel_creds, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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=False, + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=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 has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - 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, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) @pytest.mark.parametrize( @@ -295,8 +370,7 @@ def test_versions_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -322,8 +396,7 @@ def test_versions_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -340,8 +413,7 @@ def test_versions_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -359,7 +431,7 @@ def test_list_versions( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_versions), "__call__") as call: + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = version.ListVersionsResponse( next_page_token="next_page_token_value", @@ -374,6 +446,7 @@ def test_list_versions( assert args[0] == version.ListVersionsRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListVersionsPager) assert response.next_page_token == "next_page_token_value" @@ -384,19 +457,19 @@ def test_list_versions_from_dict(): @pytest.mark.asyncio -async def test_list_versions_async(transport: str = "grpc_asyncio"): +async def test_list_versions_async( + transport: str = "grpc_asyncio", request_type=version.ListVersionsRequest +): client = VersionsAsyncClient( 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 = version.ListVersionsRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_versions), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( version.ListVersionsResponse(next_page_token="next_page_token_value",) @@ -408,7 +481,7 @@ async def test_list_versions_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == version.ListVersionsRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListVersionsAsyncPager) @@ -416,6 +489,11 @@ async def test_list_versions_async(transport: str = "grpc_asyncio"): assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_list_versions_async_from_dict(): + await test_list_versions_async(request_type=dict) + + def test_list_versions_field_headers(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) @@ -425,7 +503,7 @@ def test_list_versions_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_versions), "__call__") as call: + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: call.return_value = version.ListVersionsResponse() client.list_versions(request) @@ -450,9 +528,7 @@ async def test_list_versions_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_versions), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( version.ListVersionsResponse() ) @@ -473,7 +549,7 @@ def test_list_versions_flattened(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_versions), "__call__") as call: + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = version.ListVersionsResponse() @@ -505,9 +581,7 @@ async def test_list_versions_flattened_async(): client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_versions), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = version.ListVersionsResponse() @@ -542,7 +616,7 @@ def test_list_versions_pager(): client = VersionsClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_versions), "__call__") as call: + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( version.ListVersionsResponse( @@ -576,7 +650,7 @@ def test_list_versions_pages(): client = VersionsClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_versions), "__call__") as call: + with mock.patch.object(type(client.transport.list_versions), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( version.ListVersionsResponse( @@ -603,9 +677,7 @@ async def test_list_versions_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_versions), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_versions), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -638,9 +710,7 @@ async def test_list_versions_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_versions), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_versions), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -674,7 +744,7 @@ def test_get_version(transport: str = "grpc", request_type=version.GetVersionReq request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_version), "__call__") as call: + with mock.patch.object(type(client.transport.get_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = version.Version( name="name_value", @@ -692,6 +762,7 @@ def test_get_version(transport: str = "grpc", request_type=version.GetVersionReq assert args[0] == version.GetVersionRequest() # Establish that the response is the type that we expect. + assert isinstance(response, version.Version) assert response.name == "name_value" @@ -708,19 +779,19 @@ def test_get_version_from_dict(): @pytest.mark.asyncio -async def test_get_version_async(transport: str = "grpc_asyncio"): +async def test_get_version_async( + transport: str = "grpc_asyncio", request_type=version.GetVersionRequest +): client = VersionsAsyncClient( 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 = version.GetVersionRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( version.Version( @@ -737,7 +808,7 @@ async def test_get_version_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == version.GetVersionRequest() # Establish that the response is the type that we expect. assert isinstance(response, version.Version) @@ -751,6 +822,11 @@ async def test_get_version_async(transport: str = "grpc_asyncio"): assert response.state == version.Version.State.RUNNING +@pytest.mark.asyncio +async def test_get_version_async_from_dict(): + await test_get_version_async(request_type=dict) + + def test_get_version_field_headers(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) @@ -760,7 +836,7 @@ def test_get_version_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_version), "__call__") as call: + with mock.patch.object(type(client.transport.get_version), "__call__") as call: call.return_value = version.Version() client.get_version(request) @@ -785,9 +861,7 @@ async def test_get_version_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_version), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(version.Version()) await client.get_version(request) @@ -806,7 +880,7 @@ def test_get_version_flattened(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_version), "__call__") as call: + with mock.patch.object(type(client.transport.get_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = version.Version() @@ -838,9 +912,7 @@ async def test_get_version_flattened_async(): client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = version.Version() @@ -881,7 +953,7 @@ def test_create_version( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_version), "__call__") as call: + with mock.patch.object(type(client.transport.create_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/spam") @@ -902,19 +974,19 @@ def test_create_version_from_dict(): @pytest.mark.asyncio -async def test_create_version_async(transport: str = "grpc_asyncio"): +async def test_create_version_async( + transport: str = "grpc_asyncio", request_type=gcdc_version.CreateVersionRequest +): client = VersionsAsyncClient( 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 = gcdc_version.CreateVersionRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/spam") @@ -926,12 +998,17 @@ async def test_create_version_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_version.CreateVersionRequest() # Establish that the response is the type that we expect. assert isinstance(response, future.Future) +@pytest.mark.asyncio +async def test_create_version_async_from_dict(): + await test_create_version_async(request_type=dict) + + def test_create_version_field_headers(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) @@ -941,7 +1018,7 @@ def test_create_version_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_version), "__call__") as call: + with mock.patch.object(type(client.transport.create_version), "__call__") as call: call.return_value = operations_pb2.Operation(name="operations/op") client.create_version(request) @@ -966,9 +1043,7 @@ async def test_create_version_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_version), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/op") ) @@ -989,7 +1064,7 @@ def test_create_version_flattened(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_version), "__call__") as call: + with mock.patch.object(type(client.transport.create_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1027,9 +1102,7 @@ async def test_create_version_flattened_async(): client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1078,7 +1151,7 @@ def test_update_version( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_version), "__call__") as call: + with mock.patch.object(type(client.transport.update_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_version.Version( name="name_value", @@ -1096,6 +1169,7 @@ def test_update_version( assert args[0] == gcdc_version.UpdateVersionRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_version.Version) assert response.name == "name_value" @@ -1112,19 +1186,19 @@ def test_update_version_from_dict(): @pytest.mark.asyncio -async def test_update_version_async(transport: str = "grpc_asyncio"): +async def test_update_version_async( + transport: str = "grpc_asyncio", request_type=gcdc_version.UpdateVersionRequest +): client = VersionsAsyncClient( 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 = gcdc_version.UpdateVersionRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_version.Version( @@ -1141,7 +1215,7 @@ async def test_update_version_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_version.UpdateVersionRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_version.Version) @@ -1155,6 +1229,11 @@ async def test_update_version_async(transport: str = "grpc_asyncio"): assert response.state == gcdc_version.Version.State.RUNNING +@pytest.mark.asyncio +async def test_update_version_async_from_dict(): + await test_update_version_async(request_type=dict) + + def test_update_version_field_headers(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) @@ -1164,7 +1243,7 @@ def test_update_version_field_headers(): request.version.name = "version.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_version), "__call__") as call: + with mock.patch.object(type(client.transport.update_version), "__call__") as call: call.return_value = gcdc_version.Version() client.update_version(request) @@ -1191,9 +1270,7 @@ async def test_update_version_field_headers_async(): request.version.name = "version.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_version), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_version.Version() ) @@ -1216,7 +1293,7 @@ def test_update_version_flattened(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_version), "__call__") as call: + with mock.patch.object(type(client.transport.update_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_version.Version() @@ -1255,9 +1332,7 @@ async def test_update_version_flattened_async(): client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_version.Version() @@ -1307,7 +1382,7 @@ def test_delete_version( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_version), "__call__") as call: + with mock.patch.object(type(client.transport.delete_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1328,19 +1403,19 @@ def test_delete_version_from_dict(): @pytest.mark.asyncio -async def test_delete_version_async(transport: str = "grpc_asyncio"): +async def test_delete_version_async( + transport: str = "grpc_asyncio", request_type=version.DeleteVersionRequest +): client = VersionsAsyncClient( 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 = version.DeleteVersionRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1350,12 +1425,17 @@ async def test_delete_version_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == version.DeleteVersionRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_delete_version_async_from_dict(): + await test_delete_version_async(request_type=dict) + + def test_delete_version_field_headers(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) @@ -1365,7 +1445,7 @@ def test_delete_version_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_version), "__call__") as call: + with mock.patch.object(type(client.transport.delete_version), "__call__") as call: call.return_value = None client.delete_version(request) @@ -1390,9 +1470,7 @@ async def test_delete_version_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_version), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) await client.delete_version(request) @@ -1411,7 +1489,7 @@ def test_delete_version_flattened(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_version), "__call__") as call: + with mock.patch.object(type(client.transport.delete_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1443,9 +1521,7 @@ async def test_delete_version_flattened_async(): client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1484,7 +1560,7 @@ def test_load_version(transport: str = "grpc", request_type=version.LoadVersionR request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.load_version), "__call__") as call: + with mock.patch.object(type(client.transport.load_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/spam") @@ -1505,19 +1581,19 @@ def test_load_version_from_dict(): @pytest.mark.asyncio -async def test_load_version_async(transport: str = "grpc_asyncio"): +async def test_load_version_async( + transport: str = "grpc_asyncio", request_type=version.LoadVersionRequest +): client = VersionsAsyncClient( 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 = version.LoadVersionRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.load_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.load_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/spam") @@ -1529,12 +1605,17 @@ async def test_load_version_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == version.LoadVersionRequest() # Establish that the response is the type that we expect. assert isinstance(response, future.Future) +@pytest.mark.asyncio +async def test_load_version_async_from_dict(): + await test_load_version_async(request_type=dict) + + def test_load_version_field_headers(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) @@ -1544,7 +1625,7 @@ def test_load_version_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.load_version), "__call__") as call: + with mock.patch.object(type(client.transport.load_version), "__call__") as call: call.return_value = operations_pb2.Operation(name="operations/op") client.load_version(request) @@ -1569,9 +1650,7 @@ async def test_load_version_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.load_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.load_version), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( operations_pb2.Operation(name="operations/op") ) @@ -1592,7 +1671,7 @@ def test_load_version_flattened(): client = VersionsClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.load_version), "__call__") as call: + with mock.patch.object(type(client.transport.load_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1624,9 +1703,7 @@ async def test_load_version_flattened_async(): client = VersionsAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.load_version), "__call__" - ) as call: + with mock.patch.object(type(client.transport.load_version), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = operations_pb2.Operation(name="operations/op") @@ -1693,7 +1770,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = VersionsClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -1711,10 +1788,22 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [transports.VersionsGrpcTransport, transports.VersionsGrpcAsyncIOTransport], +) +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 = VersionsClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.VersionsGrpcTransport,) + assert isinstance(client.transport, transports.VersionsGrpcTransport,) def test_versions_base_transport_error(): @@ -1778,6 +1867,17 @@ def test_versions_base_transport_with_credentials_file(): ) +def test_versions_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.dialogflowcx_v3beta1.services.versions.transports.VersionsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.VersionsTransport() + adc.assert_called_once() + + def test_versions_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -1816,7 +1916,7 @@ def test_versions_host_no_port(): api_endpoint="dialogflow.googleapis.com" ), ) - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_versions_host_with_port(): @@ -1826,204 +1926,124 @@ def test_versions_host_with_port(): api_endpoint="dialogflow.googleapis.com:8000" ), ) - assert client._transport._host == "dialogflow.googleapis.com:8000" + assert client.transport._host == "dialogflow.googleapis.com:8000" def test_versions_grpc_transport_channel(): channel = grpc.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.VersionsGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_versions_grpc_asyncio_transport_channel(): channel = aio.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.VersionsGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_versions_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.VersionsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_versions_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.VersionsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == None @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.VersionsGrpcTransport, transports.VersionsGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_versions_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.VersionsGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel +def test_versions_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.VersionsGrpcTransport, transports.VersionsGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_versions_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials class. +def test_versions_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), ): - mock_cred = mock.Mock() - transport = transports.VersionsGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + with mock.patch.object( + transport_class, "create_channel", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel def test_versions_grpc_lro_client(): client = VersionsClient( credentials=credentials.AnonymousCredentials(), transport="grpc", ) - transport = client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsClient,) @@ -2036,7 +2056,7 @@ def test_versions_grpc_lro_async_client(): client = VersionsAsyncClient( credentials=credentials.AnonymousCredentials(), transport="grpc_asyncio", ) - transport = client._client._transport + transport = client.transport # Ensure that we have a api-core operations client. assert isinstance(transport.operations_client, operations_v1.OperationsAsyncClient,) @@ -2074,6 +2094,107 @@ def test_parse_version_path(): assert expected == actual +def test_common_billing_account_path(): + billing_account = "scallop" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = VersionsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "abalone", + } + path = VersionsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = VersionsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "squid" + + expected = "folders/{folder}".format(folder=folder,) + actual = VersionsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "clam", + } + path = VersionsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = VersionsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "whelk" + + expected = "organizations/{organization}".format(organization=organization,) + actual = VersionsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "octopus", + } + path = VersionsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = VersionsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "oyster" + + expected = "projects/{project}".format(project=project,) + actual = VersionsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nudibranch", + } + path = VersionsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = VersionsClient.parse_common_project_path(path) + assert expected == actual + + +def test_common_location_path(): + project = "cuttlefish" + location = "mussel" + + expected = "projects/{project}/locations/{location}".format( + project=project, location=location, + ) + actual = VersionsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "winkle", + "location": "nautilus", + } + path = VersionsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = VersionsClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo() diff --git a/tests/unit/gapic/dialogflowcx_v3beta1/test_webhooks.py b/tests/unit/gapic/dialogflowcx_v3beta1/test_webhooks.py index 0f7cf79f..b4c3031b 100644 --- a/tests/unit/gapic/dialogflowcx_v3beta1/test_webhooks.py +++ b/tests/unit/gapic/dialogflowcx_v3beta1/test_webhooks.py @@ -90,12 +90,12 @@ def test_webhooks_client_from_service_account_file(client_class): ) as factory: factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds client = client_class.from_service_account_json("dummy/file/path.json") - assert client._transport._credentials == creds + assert client.transport._credentials == creds - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_webhooks_client_get_transport_class(): @@ -143,15 +143,14 @@ def test_webhooks_client_client_options(client_class, transport_class, transport credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -160,15 +159,14 @@ def test_webhooks_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=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 is + # 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": "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() @@ -177,95 +175,173 @@ def test_webhooks_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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, + ssl_channel_credentials=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", + [ + (WebhooksClient, transports.WebhooksGrpcTransport, "grpc", "true"), + ( + WebhooksAsyncClient, + transports.WebhooksGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (WebhooksClient, transports.WebhooksGrpcTransport, "grpc", "false"), + ( + WebhooksAsyncClient, + transports.WebhooksGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + WebhooksClient, "DEFAULT_ENDPOINT", modify_default_endpoint(WebhooksClient) +) +@mock.patch.object( + WebhooksAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(WebhooksAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_webhooks_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) - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=client_cert_source_callback, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) - - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", and default_client_cert_source is provided. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): - with mock.patch.object(transport_class, "__init__") as patched: + ssl_channel_creds = mock.Mock() with mock.patch( - "google.auth.transport.mtls.has_default_client_cert_source", - return_value=True, + "grpc.ssl_channel_credentials", return_value=ssl_channel_creds ): patched.return_value = None - client = client_class() + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_ssl_channel_creds = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_ssl_channel_creds = ssl_channel_creds + expected_host = client.DEFAULT_MTLS_ENDPOINT + patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_MTLS_ENDPOINT, + host=expected_host, scopes=None, - api_mtls_endpoint=client.DEFAULT_MTLS_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=expected_ssl_channel_creds, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) - # Check the case api_endpoint is not provided, GOOGLE_API_USE_MTLS is - # "auto", but client_cert_source and default_client_cert_source are None. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "auto"}): + # 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=False, + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None ): - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=client.DEFAULT_ENDPOINT, - scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=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 has - # unsupported value. - with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS": "Unsupported"}): - with pytest.raises(MutualTLSChannelError): - 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, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, - quota_project_id="octopus", - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.ssl_credentials", + new_callable=mock.PropertyMock, + ) as ssl_credentials_mock: + if use_client_cert_env == "false": + is_mtls_mock.return_value = False + ssl_credentials_mock.return_value = None + expected_host = client.DEFAULT_ENDPOINT + expected_ssl_channel_creds = None + else: + is_mtls_mock.return_value = True + ssl_credentials_mock.return_value = mock.Mock() + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_ssl_channel_creds = ( + ssl_credentials_mock.return_value + ) + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + ssl_channel_credentials=expected_ssl_channel_creds, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + ): + with mock.patch( + "google.auth.transport.grpc.SslCredentials.is_mtls", + new_callable=mock.PropertyMock, + ) as is_mtls_mock: + is_mtls_mock.return_value = False + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + ssl_channel_credentials=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) @pytest.mark.parametrize( @@ -288,8 +364,7 @@ def test_webhooks_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -315,8 +390,7 @@ def test_webhooks_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - api_mtls_endpoint=client.DEFAULT_ENDPOINT, - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -333,8 +407,7 @@ def test_webhooks_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - api_mtls_endpoint="squid.clam.whelk", - client_cert_source=None, + ssl_channel_credentials=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -352,7 +425,7 @@ def test_list_webhooks( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_webhooks), "__call__") as call: + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = webhook.ListWebhooksResponse( next_page_token="next_page_token_value", @@ -367,6 +440,7 @@ def test_list_webhooks( assert args[0] == webhook.ListWebhooksRequest() # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListWebhooksPager) assert response.next_page_token == "next_page_token_value" @@ -377,19 +451,19 @@ def test_list_webhooks_from_dict(): @pytest.mark.asyncio -async def test_list_webhooks_async(transport: str = "grpc_asyncio"): +async def test_list_webhooks_async( + transport: str = "grpc_asyncio", request_type=webhook.ListWebhooksRequest +): client = WebhooksAsyncClient( 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 = webhook.ListWebhooksRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_webhooks), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( webhook.ListWebhooksResponse(next_page_token="next_page_token_value",) @@ -401,7 +475,7 @@ async def test_list_webhooks_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == webhook.ListWebhooksRequest() # Establish that the response is the type that we expect. assert isinstance(response, pagers.ListWebhooksAsyncPager) @@ -409,6 +483,11 @@ async def test_list_webhooks_async(transport: str = "grpc_asyncio"): assert response.next_page_token == "next_page_token_value" +@pytest.mark.asyncio +async def test_list_webhooks_async_from_dict(): + await test_list_webhooks_async(request_type=dict) + + def test_list_webhooks_field_headers(): client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) @@ -418,7 +497,7 @@ def test_list_webhooks_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_webhooks), "__call__") as call: + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: call.return_value = webhook.ListWebhooksResponse() client.list_webhooks(request) @@ -443,9 +522,7 @@ async def test_list_webhooks_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_webhooks), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( webhook.ListWebhooksResponse() ) @@ -466,7 +543,7 @@ def test_list_webhooks_flattened(): client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_webhooks), "__call__") as call: + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = webhook.ListWebhooksResponse() @@ -498,9 +575,7 @@ async def test_list_webhooks_flattened_async(): client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.list_webhooks), "__call__" - ) as call: + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = webhook.ListWebhooksResponse() @@ -535,7 +610,7 @@ def test_list_webhooks_pager(): client = WebhooksClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_webhooks), "__call__") as call: + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( webhook.ListWebhooksResponse( @@ -569,7 +644,7 @@ def test_list_webhooks_pages(): client = WebhooksClient(credentials=credentials.AnonymousCredentials,) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.list_webhooks), "__call__") as call: + with mock.patch.object(type(client.transport.list_webhooks), "__call__") as call: # Set the response to a series of pages. call.side_effect = ( webhook.ListWebhooksResponse( @@ -596,9 +671,7 @@ async def test_list_webhooks_async_pager(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_webhooks), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_webhooks), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -631,9 +704,7 @@ async def test_list_webhooks_async_pages(): # Mock the actual call within the gRPC stub, and fake the request. with mock.patch.object( - type(client._client._transport.list_webhooks), - "__call__", - new_callable=mock.AsyncMock, + type(client.transport.list_webhooks), "__call__", new_callable=mock.AsyncMock ) as call: # Set the response to a series of pages. call.side_effect = ( @@ -667,7 +738,7 @@ def test_get_webhook(transport: str = "grpc", request_type=webhook.GetWebhookReq request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.get_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = webhook.Webhook( name="name_value", @@ -685,6 +756,7 @@ def test_get_webhook(transport: str = "grpc", request_type=webhook.GetWebhookReq assert args[0] == webhook.GetWebhookRequest() # Establish that the response is the type that we expect. + assert isinstance(response, webhook.Webhook) assert response.name == "name_value" @@ -699,19 +771,19 @@ def test_get_webhook_from_dict(): @pytest.mark.asyncio -async def test_get_webhook_async(transport: str = "grpc_asyncio"): +async def test_get_webhook_async( + transport: str = "grpc_asyncio", request_type=webhook.GetWebhookRequest +): client = WebhooksAsyncClient( 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 = webhook.GetWebhookRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( webhook.Webhook( @@ -725,7 +797,7 @@ async def test_get_webhook_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == webhook.GetWebhookRequest() # Establish that the response is the type that we expect. assert isinstance(response, webhook.Webhook) @@ -737,6 +809,11 @@ async def test_get_webhook_async(transport: str = "grpc_asyncio"): assert response.disabled is True +@pytest.mark.asyncio +async def test_get_webhook_async_from_dict(): + await test_get_webhook_async(request_type=dict) + + def test_get_webhook_field_headers(): client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) @@ -746,7 +823,7 @@ def test_get_webhook_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.get_webhook), "__call__") as call: call.return_value = webhook.Webhook() client.get_webhook(request) @@ -771,9 +848,7 @@ async def test_get_webhook_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_webhook), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(webhook.Webhook()) await client.get_webhook(request) @@ -792,7 +867,7 @@ def test_get_webhook_flattened(): client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.get_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.get_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = webhook.Webhook() @@ -824,9 +899,7 @@ async def test_get_webhook_flattened_async(): client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.get_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.get_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = webhook.Webhook() @@ -867,7 +940,7 @@ def test_create_webhook( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.create_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_webhook.Webhook( name="name_value", @@ -885,6 +958,7 @@ def test_create_webhook( assert args[0] == gcdc_webhook.CreateWebhookRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_webhook.Webhook) assert response.name == "name_value" @@ -899,19 +973,19 @@ def test_create_webhook_from_dict(): @pytest.mark.asyncio -async def test_create_webhook_async(transport: str = "grpc_asyncio"): +async def test_create_webhook_async( + transport: str = "grpc_asyncio", request_type=gcdc_webhook.CreateWebhookRequest +): client = WebhooksAsyncClient( 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 = gcdc_webhook.CreateWebhookRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_webhook.Webhook( @@ -925,7 +999,7 @@ async def test_create_webhook_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_webhook.CreateWebhookRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_webhook.Webhook) @@ -937,6 +1011,11 @@ async def test_create_webhook_async(transport: str = "grpc_asyncio"): assert response.disabled is True +@pytest.mark.asyncio +async def test_create_webhook_async_from_dict(): + await test_create_webhook_async(request_type=dict) + + def test_create_webhook_field_headers(): client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) @@ -946,7 +1025,7 @@ def test_create_webhook_field_headers(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.create_webhook), "__call__") as call: call.return_value = gcdc_webhook.Webhook() client.create_webhook(request) @@ -971,9 +1050,7 @@ async def test_create_webhook_field_headers_async(): request.parent = "parent/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_webhook), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_webhook.Webhook() ) @@ -994,7 +1071,7 @@ def test_create_webhook_flattened(): client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.create_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.create_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_webhook.Webhook() @@ -1032,9 +1109,7 @@ async def test_create_webhook_flattened_async(): client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.create_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.create_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_webhook.Webhook() @@ -1083,7 +1158,7 @@ def test_update_webhook( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.update_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_webhook.Webhook( name="name_value", @@ -1101,6 +1176,7 @@ def test_update_webhook( assert args[0] == gcdc_webhook.UpdateWebhookRequest() # Establish that the response is the type that we expect. + assert isinstance(response, gcdc_webhook.Webhook) assert response.name == "name_value" @@ -1115,19 +1191,19 @@ def test_update_webhook_from_dict(): @pytest.mark.asyncio -async def test_update_webhook_async(transport: str = "grpc_asyncio"): +async def test_update_webhook_async( + transport: str = "grpc_asyncio", request_type=gcdc_webhook.UpdateWebhookRequest +): client = WebhooksAsyncClient( 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 = gcdc_webhook.UpdateWebhookRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_webhook.Webhook( @@ -1141,7 +1217,7 @@ async def test_update_webhook_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == gcdc_webhook.UpdateWebhookRequest() # Establish that the response is the type that we expect. assert isinstance(response, gcdc_webhook.Webhook) @@ -1153,6 +1229,11 @@ async def test_update_webhook_async(transport: str = "grpc_asyncio"): assert response.disabled is True +@pytest.mark.asyncio +async def test_update_webhook_async_from_dict(): + await test_update_webhook_async(request_type=dict) + + def test_update_webhook_field_headers(): client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) @@ -1162,7 +1243,7 @@ def test_update_webhook_field_headers(): request.webhook.name = "webhook.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.update_webhook), "__call__") as call: call.return_value = gcdc_webhook.Webhook() client.update_webhook(request) @@ -1189,9 +1270,7 @@ async def test_update_webhook_field_headers_async(): request.webhook.name = "webhook.name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_webhook), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( gcdc_webhook.Webhook() ) @@ -1214,7 +1293,7 @@ def test_update_webhook_flattened(): client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.update_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.update_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_webhook.Webhook() @@ -1253,9 +1332,7 @@ async def test_update_webhook_flattened_async(): client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.update_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.update_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = gcdc_webhook.Webhook() @@ -1305,7 +1382,7 @@ def test_delete_webhook( request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.delete_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1326,19 +1403,19 @@ def test_delete_webhook_from_dict(): @pytest.mark.asyncio -async def test_delete_webhook_async(transport: str = "grpc_asyncio"): +async def test_delete_webhook_async( + transport: str = "grpc_asyncio", request_type=webhook.DeleteWebhookRequest +): client = WebhooksAsyncClient( 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 = webhook.DeleteWebhookRequest() + request = request_type() # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) @@ -1348,12 +1425,17 @@ async def test_delete_webhook_async(transport: str = "grpc_asyncio"): assert len(call.mock_calls) _, args, _ = call.mock_calls[0] - assert args[0] == request + assert args[0] == webhook.DeleteWebhookRequest() # Establish that the response is the type that we expect. assert response is None +@pytest.mark.asyncio +async def test_delete_webhook_async_from_dict(): + await test_delete_webhook_async(request_type=dict) + + def test_delete_webhook_field_headers(): client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) @@ -1363,7 +1445,7 @@ def test_delete_webhook_field_headers(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.delete_webhook), "__call__") as call: call.return_value = None client.delete_webhook(request) @@ -1388,9 +1470,7 @@ async def test_delete_webhook_field_headers_async(): request.name = "name/value" # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_webhook), "__call__") as call: call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) await client.delete_webhook(request) @@ -1409,7 +1489,7 @@ def test_delete_webhook_flattened(): client = WebhooksClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object(type(client._transport.delete_webhook), "__call__") as call: + with mock.patch.object(type(client.transport.delete_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1441,9 +1521,7 @@ async def test_delete_webhook_flattened_async(): client = WebhooksAsyncClient(credentials=credentials.AnonymousCredentials(),) # Mock the actual call within the gRPC stub, and fake the request. - with mock.patch.object( - type(client._client._transport.delete_webhook), "__call__" - ) as call: + with mock.patch.object(type(client.transport.delete_webhook), "__call__") as call: # Designate an appropriate return value for the call. call.return_value = None @@ -1508,7 +1586,7 @@ def test_transport_instance(): credentials=credentials.AnonymousCredentials(), ) client = WebhooksClient(transport=transport) - assert client._transport is transport + assert client.transport is transport def test_transport_get_channel(): @@ -1526,10 +1604,22 @@ def test_transport_get_channel(): assert channel +@pytest.mark.parametrize( + "transport_class", + [transports.WebhooksGrpcTransport, transports.WebhooksGrpcAsyncIOTransport], +) +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 = WebhooksClient(credentials=credentials.AnonymousCredentials(),) - assert isinstance(client._transport, transports.WebhooksGrpcTransport,) + assert isinstance(client.transport, transports.WebhooksGrpcTransport,) def test_webhooks_base_transport_error(): @@ -1587,6 +1677,17 @@ def test_webhooks_base_transport_with_credentials_file(): ) +def test_webhooks_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.dialogflowcx_v3beta1.services.webhooks.transports.WebhooksTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.WebhooksTransport() + adc.assert_called_once() + + def test_webhooks_auth_adc(): # If no credentials are provided, we should use ADC credentials. with mock.patch.object(auth, "default") as adc: @@ -1625,7 +1726,7 @@ def test_webhooks_host_no_port(): api_endpoint="dialogflow.googleapis.com" ), ) - assert client._transport._host == "dialogflow.googleapis.com:443" + assert client.transport._host == "dialogflow.googleapis.com:443" def test_webhooks_host_with_port(): @@ -1635,197 +1736,117 @@ def test_webhooks_host_with_port(): api_endpoint="dialogflow.googleapis.com:8000" ), ) - assert client._transport._host == "dialogflow.googleapis.com:8000" + assert client.transport._host == "dialogflow.googleapis.com:8000" def test_webhooks_grpc_transport_channel(): channel = grpc.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.WebhooksGrpcTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called + assert transport._ssl_channel_credentials == None def test_webhooks_grpc_asyncio_transport_channel(): channel = aio.insecure_channel("http://localhost/") - # Check that if channel is provided, mtls endpoint and client_cert_source - # won't be used. - callback = mock.MagicMock() + # Check that channel is used if provided. transport = transports.WebhooksGrpcAsyncIOTransport( - host="squid.clam.whelk", - channel=channel, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=callback, + host="squid.clam.whelk", channel=channel, ) assert transport.grpc_channel == channel assert transport._host == "squid.clam.whelk:443" - assert not callback.called - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_webhooks_grpc_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.WebhooksGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel - - -@mock.patch("grpc.ssl_channel_credentials", autospec=True) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_webhooks_grpc_asyncio_transport_channel_mtls_with_client_cert_source( - grpc_create_channel, grpc_ssl_channel_cred -): - # Check that if channel is None, but api_mtls_endpoint and client_cert_source - # are provided, then a mTLS channel will be created. - mock_cred = mock.Mock() - - 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 - - transport = transports.WebhooksGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint="mtls.squid.clam.whelk", - client_cert_source=client_cert_source_callback, - ) - 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=mock_cred, - credentials_file=None, - scopes=( - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == None @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.WebhooksGrpcTransport, transports.WebhooksGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers.create_channel", autospec=True) -def test_webhooks_grpc_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials 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), - ): - mock_cred = mock.Mock() - transport = transports.WebhooksGrpcTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel +def test_webhooks_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", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred @pytest.mark.parametrize( - "api_mtls_endpoint", ["mtls.squid.clam.whelk", "mtls.squid.clam.whelk:443"] + "transport_class", + [transports.WebhooksGrpcTransport, transports.WebhooksGrpcAsyncIOTransport], ) -@mock.patch("google.api_core.grpc_helpers_async.create_channel", autospec=True) -def test_webhooks_grpc_asyncio_transport_channel_mtls_with_adc( - grpc_create_channel, api_mtls_endpoint -): - # Check that if channel and client_cert_source are None, but api_mtls_endpoint - # is provided, then a mTLS channel will be created with SSL ADC. - mock_grpc_channel = mock.Mock() - grpc_create_channel.return_value = mock_grpc_channel - - # Mock google.auth.transport.grpc.SslCredentials class. +def test_webhooks_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), ): - mock_cred = mock.Mock() - transport = transports.WebhooksGrpcAsyncIOTransport( - host="squid.clam.whelk", - credentials=mock_cred, - api_mtls_endpoint=api_mtls_endpoint, - 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", - "https://www.googleapis.com/auth/dialogflow", - ), - ssl_credentials=mock_ssl_cred, - quota_project_id=None, - ) - assert transport.grpc_channel == mock_grpc_channel + with mock.patch.object( + transport_class, "create_channel", autospec=True + ) 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", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + ) + assert transport.grpc_channel == mock_grpc_channel def test_webhook_path(): @@ -1855,6 +1876,107 @@ def test_parse_webhook_path(): assert expected == actual +def test_common_billing_account_path(): + billing_account = "winkle" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = WebhooksClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nautilus", + } + path = WebhooksClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = WebhooksClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "scallop" + + expected = "folders/{folder}".format(folder=folder,) + actual = WebhooksClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "abalone", + } + path = WebhooksClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = WebhooksClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "squid" + + expected = "organizations/{organization}".format(organization=organization,) + actual = WebhooksClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "clam", + } + path = WebhooksClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = WebhooksClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "whelk" + + expected = "projects/{project}".format(project=project,) + actual = WebhooksClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "octopus", + } + path = WebhooksClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = WebhooksClient.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 = WebhooksClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + } + path = WebhooksClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = WebhooksClient.parse_common_location_path(path) + assert expected == actual + + def test_client_withDEFAULT_CLIENT_INFO(): client_info = gapic_v1.client_info.ClientInfo()