diff --git a/.coveragerc b/.coveragerc index 6bf364f90..3ce461881 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,27 +1,11 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Generated by synthtool. DO NOT EDIT! [run] branch = True [report] fail_under = 100 show_missing = True -omit = google/cloud/dialogflow/__init__.py +omit = + google/cloud/dialogflow/__init__.py exclude_lines = # Re-enable the standard pragma pragma: NO COVER @@ -31,4 +15,4 @@ exclude_lines = # This is added at the module level as a safeguard for if someone # generates the code and tries to run it without pip installing. This # makes it virtually impossible to test properly. - except pkg_resources.DistributionNotFound \ No newline at end of file + except pkg_resources.DistributionNotFound diff --git a/docs/dialogflow_v2/agents.rst b/docs/dialogflow_v2/agents.rst new file mode 100644 index 000000000..63897cb22 --- /dev/null +++ b/docs/dialogflow_v2/agents.rst @@ -0,0 +1,11 @@ +Agents +------------------------ + +.. automodule:: google.cloud.dialogflow_v2.services.agents + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.agents.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/answer_records.rst b/docs/dialogflow_v2/answer_records.rst new file mode 100644 index 000000000..02a390a71 --- /dev/null +++ b/docs/dialogflow_v2/answer_records.rst @@ -0,0 +1,11 @@ +AnswerRecords +------------------------------- + +.. automodule:: google.cloud.dialogflow_v2.services.answer_records + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.answer_records.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/contexts.rst b/docs/dialogflow_v2/contexts.rst new file mode 100644 index 000000000..2f4c1efd7 --- /dev/null +++ b/docs/dialogflow_v2/contexts.rst @@ -0,0 +1,11 @@ +Contexts +-------------------------- + +.. automodule:: google.cloud.dialogflow_v2.services.contexts + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.contexts.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/conversation_profiles.rst b/docs/dialogflow_v2/conversation_profiles.rst new file mode 100644 index 000000000..85eaab68d --- /dev/null +++ b/docs/dialogflow_v2/conversation_profiles.rst @@ -0,0 +1,11 @@ +ConversationProfiles +-------------------------------------- + +.. automodule:: google.cloud.dialogflow_v2.services.conversation_profiles + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.conversation_profiles.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/conversations.rst b/docs/dialogflow_v2/conversations.rst new file mode 100644 index 000000000..72a2eb66c --- /dev/null +++ b/docs/dialogflow_v2/conversations.rst @@ -0,0 +1,11 @@ +Conversations +------------------------------- + +.. automodule:: google.cloud.dialogflow_v2.services.conversations + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.conversations.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/documents.rst b/docs/dialogflow_v2/documents.rst new file mode 100644 index 000000000..fa18a2320 --- /dev/null +++ b/docs/dialogflow_v2/documents.rst @@ -0,0 +1,11 @@ +Documents +--------------------------- + +.. automodule:: google.cloud.dialogflow_v2.services.documents + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.documents.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/entity_types.rst b/docs/dialogflow_v2/entity_types.rst new file mode 100644 index 000000000..821b7fd71 --- /dev/null +++ b/docs/dialogflow_v2/entity_types.rst @@ -0,0 +1,11 @@ +EntityTypes +----------------------------- + +.. automodule:: google.cloud.dialogflow_v2.services.entity_types + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.entity_types.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/environments.rst b/docs/dialogflow_v2/environments.rst new file mode 100644 index 000000000..3e769ee5d --- /dev/null +++ b/docs/dialogflow_v2/environments.rst @@ -0,0 +1,11 @@ +Environments +------------------------------ + +.. automodule:: google.cloud.dialogflow_v2.services.environments + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.environments.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/intents.rst b/docs/dialogflow_v2/intents.rst new file mode 100644 index 000000000..c2a444ebf --- /dev/null +++ b/docs/dialogflow_v2/intents.rst @@ -0,0 +1,11 @@ +Intents +------------------------- + +.. automodule:: google.cloud.dialogflow_v2.services.intents + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.intents.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/knowledge_bases.rst b/docs/dialogflow_v2/knowledge_bases.rst new file mode 100644 index 000000000..d3371043f --- /dev/null +++ b/docs/dialogflow_v2/knowledge_bases.rst @@ -0,0 +1,11 @@ +KnowledgeBases +-------------------------------- + +.. automodule:: google.cloud.dialogflow_v2.services.knowledge_bases + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.knowledge_bases.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/participants.rst b/docs/dialogflow_v2/participants.rst new file mode 100644 index 000000000..9f5eecedb --- /dev/null +++ b/docs/dialogflow_v2/participants.rst @@ -0,0 +1,11 @@ +Participants +------------------------------ + +.. automodule:: google.cloud.dialogflow_v2.services.participants + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.participants.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/services.rst b/docs/dialogflow_v2/services.rst index 69a3904d0..66405aac5 100644 --- a/docs/dialogflow_v2/services.rst +++ b/docs/dialogflow_v2/services.rst @@ -1,24 +1,18 @@ Services for Google Cloud Dialogflow v2 API =========================================== +.. toctree:: + :maxdepth: 2 -.. automodule:: google.cloud.dialogflow_v2.services.agents - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2.services.contexts - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2.services.entity_types - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2.services.environments - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2.services.intents - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2.services.session_entity_types - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2.services.sessions - :members: - :inherited-members: + agents + answer_records + contexts + conversation_profiles + conversations + documents + entity_types + environments + intents + knowledge_bases + participants + session_entity_types + sessions diff --git a/docs/dialogflow_v2/session_entity_types.rst b/docs/dialogflow_v2/session_entity_types.rst new file mode 100644 index 000000000..a3cfccbbd --- /dev/null +++ b/docs/dialogflow_v2/session_entity_types.rst @@ -0,0 +1,11 @@ +SessionEntityTypes +------------------------------------ + +.. automodule:: google.cloud.dialogflow_v2.services.session_entity_types + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2.services.session_entity_types.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/sessions.rst b/docs/dialogflow_v2/sessions.rst new file mode 100644 index 000000000..1da650e33 --- /dev/null +++ b/docs/dialogflow_v2/sessions.rst @@ -0,0 +1,6 @@ +Sessions +-------------------------- + +.. automodule:: google.cloud.dialogflow_v2.services.sessions + :members: + :inherited-members: diff --git a/docs/dialogflow_v2/types.rst b/docs/dialogflow_v2/types.rst index 3d0f4eb3c..205c4fadd 100644 --- a/docs/dialogflow_v2/types.rst +++ b/docs/dialogflow_v2/types.rst @@ -3,4 +3,5 @@ Types for Google Cloud Dialogflow v2 API .. automodule:: google.cloud.dialogflow_v2.types :members: + :undoc-members: :show-inheritance: diff --git a/docs/dialogflow_v2beta1/agents.rst b/docs/dialogflow_v2beta1/agents.rst new file mode 100644 index 000000000..057032e4a --- /dev/null +++ b/docs/dialogflow_v2beta1/agents.rst @@ -0,0 +1,11 @@ +Agents +------------------------ + +.. automodule:: google.cloud.dialogflow_v2beta1.services.agents + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.agents.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/answer_records.rst b/docs/dialogflow_v2beta1/answer_records.rst new file mode 100644 index 000000000..26d58d618 --- /dev/null +++ b/docs/dialogflow_v2beta1/answer_records.rst @@ -0,0 +1,11 @@ +AnswerRecords +------------------------------- + +.. automodule:: google.cloud.dialogflow_v2beta1.services.answer_records + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.answer_records.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/contexts.rst b/docs/dialogflow_v2beta1/contexts.rst new file mode 100644 index 000000000..0568b2019 --- /dev/null +++ b/docs/dialogflow_v2beta1/contexts.rst @@ -0,0 +1,11 @@ +Contexts +-------------------------- + +.. automodule:: google.cloud.dialogflow_v2beta1.services.contexts + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.contexts.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/conversation_profiles.rst b/docs/dialogflow_v2beta1/conversation_profiles.rst new file mode 100644 index 000000000..f3ecaaa53 --- /dev/null +++ b/docs/dialogflow_v2beta1/conversation_profiles.rst @@ -0,0 +1,11 @@ +ConversationProfiles +-------------------------------------- + +.. automodule:: google.cloud.dialogflow_v2beta1.services.conversation_profiles + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.conversation_profiles.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/conversations.rst b/docs/dialogflow_v2beta1/conversations.rst new file mode 100644 index 000000000..fda02a6a1 --- /dev/null +++ b/docs/dialogflow_v2beta1/conversations.rst @@ -0,0 +1,11 @@ +Conversations +------------------------------- + +.. automodule:: google.cloud.dialogflow_v2beta1.services.conversations + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.conversations.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/documents.rst b/docs/dialogflow_v2beta1/documents.rst new file mode 100644 index 000000000..9516f4254 --- /dev/null +++ b/docs/dialogflow_v2beta1/documents.rst @@ -0,0 +1,11 @@ +Documents +--------------------------- + +.. automodule:: google.cloud.dialogflow_v2beta1.services.documents + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.documents.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/entity_types.rst b/docs/dialogflow_v2beta1/entity_types.rst new file mode 100644 index 000000000..f7c940846 --- /dev/null +++ b/docs/dialogflow_v2beta1/entity_types.rst @@ -0,0 +1,11 @@ +EntityTypes +----------------------------- + +.. automodule:: google.cloud.dialogflow_v2beta1.services.entity_types + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.entity_types.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/environments.rst b/docs/dialogflow_v2beta1/environments.rst new file mode 100644 index 000000000..f55d2c24d --- /dev/null +++ b/docs/dialogflow_v2beta1/environments.rst @@ -0,0 +1,11 @@ +Environments +------------------------------ + +.. automodule:: google.cloud.dialogflow_v2beta1.services.environments + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.environments.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/intents.rst b/docs/dialogflow_v2beta1/intents.rst new file mode 100644 index 000000000..2af6dbe49 --- /dev/null +++ b/docs/dialogflow_v2beta1/intents.rst @@ -0,0 +1,11 @@ +Intents +------------------------- + +.. automodule:: google.cloud.dialogflow_v2beta1.services.intents + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.intents.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/knowledge_bases.rst b/docs/dialogflow_v2beta1/knowledge_bases.rst new file mode 100644 index 000000000..7d2696d24 --- /dev/null +++ b/docs/dialogflow_v2beta1/knowledge_bases.rst @@ -0,0 +1,11 @@ +KnowledgeBases +-------------------------------- + +.. automodule:: google.cloud.dialogflow_v2beta1.services.knowledge_bases + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.knowledge_bases.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/participants.rst b/docs/dialogflow_v2beta1/participants.rst new file mode 100644 index 000000000..61590eb9d --- /dev/null +++ b/docs/dialogflow_v2beta1/participants.rst @@ -0,0 +1,11 @@ +Participants +------------------------------ + +.. automodule:: google.cloud.dialogflow_v2beta1.services.participants + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.participants.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/services.rst b/docs/dialogflow_v2beta1/services.rst index 9350d256f..209363e0e 100644 --- a/docs/dialogflow_v2beta1/services.rst +++ b/docs/dialogflow_v2beta1/services.rst @@ -1,30 +1,18 @@ Services for Google Cloud Dialogflow v2beta1 API ================================================ +.. toctree:: + :maxdepth: 2 -.. automodule:: google.cloud.dialogflow_v2beta1.services.agents - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2beta1.services.contexts - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2beta1.services.documents - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2beta1.services.entity_types - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2beta1.services.environments - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2beta1.services.intents - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2beta1.services.knowledge_bases - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2beta1.services.session_entity_types - :members: - :inherited-members: -.. automodule:: google.cloud.dialogflow_v2beta1.services.sessions - :members: - :inherited-members: + agents + answer_records + contexts + conversation_profiles + conversations + documents + entity_types + environments + intents + knowledge_bases + participants + session_entity_types + sessions diff --git a/docs/dialogflow_v2beta1/session_entity_types.rst b/docs/dialogflow_v2beta1/session_entity_types.rst new file mode 100644 index 000000000..113b7acfb --- /dev/null +++ b/docs/dialogflow_v2beta1/session_entity_types.rst @@ -0,0 +1,11 @@ +SessionEntityTypes +------------------------------------ + +.. automodule:: google.cloud.dialogflow_v2beta1.services.session_entity_types + :members: + :inherited-members: + + +.. automodule:: google.cloud.dialogflow_v2beta1.services.session_entity_types.pagers + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/sessions.rst b/docs/dialogflow_v2beta1/sessions.rst new file mode 100644 index 000000000..da6517c63 --- /dev/null +++ b/docs/dialogflow_v2beta1/sessions.rst @@ -0,0 +1,6 @@ +Sessions +-------------------------- + +.. automodule:: google.cloud.dialogflow_v2beta1.services.sessions + :members: + :inherited-members: diff --git a/docs/dialogflow_v2beta1/types.rst b/docs/dialogflow_v2beta1/types.rst index e50759342..f4c430d5a 100644 --- a/docs/dialogflow_v2beta1/types.rst +++ b/docs/dialogflow_v2beta1/types.rst @@ -3,4 +3,5 @@ Types for Google Cloud Dialogflow v2beta1 API .. automodule:: google.cloud.dialogflow_v2beta1.types :members: + :undoc-members: :show-inheritance: diff --git a/google/cloud/dialogflow/__init__.py b/google/cloud/dialogflow/__init__.py index 0afc0f343..d84ba598d 100644 --- a/google/cloud/dialogflow/__init__.py +++ b/google/cloud/dialogflow/__init__.py @@ -17,10 +17,30 @@ from google.cloud.dialogflow_v2.services.agents.async_client import AgentsAsyncClient from google.cloud.dialogflow_v2.services.agents.client import AgentsClient +from google.cloud.dialogflow_v2.services.answer_records.async_client import ( + AnswerRecordsAsyncClient, +) +from google.cloud.dialogflow_v2.services.answer_records.client import ( + AnswerRecordsClient, +) from google.cloud.dialogflow_v2.services.contexts.async_client import ( ContextsAsyncClient, ) from google.cloud.dialogflow_v2.services.contexts.client import ContextsClient +from google.cloud.dialogflow_v2.services.conversation_profiles.async_client import ( + ConversationProfilesAsyncClient, +) +from google.cloud.dialogflow_v2.services.conversation_profiles.client import ( + ConversationProfilesClient, +) +from google.cloud.dialogflow_v2.services.conversations.async_client import ( + ConversationsAsyncClient, +) +from google.cloud.dialogflow_v2.services.conversations.client import ConversationsClient +from google.cloud.dialogflow_v2.services.documents.async_client import ( + DocumentsAsyncClient, +) +from google.cloud.dialogflow_v2.services.documents.client import DocumentsClient from google.cloud.dialogflow_v2.services.entity_types.async_client import ( EntityTypesAsyncClient, ) @@ -31,6 +51,16 @@ from google.cloud.dialogflow_v2.services.environments.client import EnvironmentsClient from google.cloud.dialogflow_v2.services.intents.async_client import IntentsAsyncClient from google.cloud.dialogflow_v2.services.intents.client import IntentsClient +from google.cloud.dialogflow_v2.services.knowledge_bases.async_client import ( + KnowledgeBasesAsyncClient, +) +from google.cloud.dialogflow_v2.services.knowledge_bases.client import ( + KnowledgeBasesClient, +) +from google.cloud.dialogflow_v2.services.participants.async_client import ( + ParticipantsAsyncClient, +) +from google.cloud.dialogflow_v2.services.participants.client import ParticipantsClient from google.cloud.dialogflow_v2.services.session_entity_types.async_client import ( SessionEntityTypesAsyncClient, ) @@ -53,15 +83,25 @@ from google.cloud.dialogflow_v2.types.agent import SearchAgentsResponse from google.cloud.dialogflow_v2.types.agent import SetAgentRequest from google.cloud.dialogflow_v2.types.agent import TrainAgentRequest +from google.cloud.dialogflow_v2.types.answer_record import AgentAssistantFeedback +from google.cloud.dialogflow_v2.types.answer_record import AgentAssistantRecord +from google.cloud.dialogflow_v2.types.answer_record import AnswerFeedback +from google.cloud.dialogflow_v2.types.answer_record import AnswerRecord +from google.cloud.dialogflow_v2.types.answer_record import ListAnswerRecordsRequest +from google.cloud.dialogflow_v2.types.answer_record import ListAnswerRecordsResponse +from google.cloud.dialogflow_v2.types.answer_record import UpdateAnswerRecordRequest from google.cloud.dialogflow_v2.types.audio_config import AudioEncoding from google.cloud.dialogflow_v2.types.audio_config import InputAudioConfig from google.cloud.dialogflow_v2.types.audio_config import OutputAudioConfig from google.cloud.dialogflow_v2.types.audio_config import OutputAudioEncoding from google.cloud.dialogflow_v2.types.audio_config import SpeechContext from google.cloud.dialogflow_v2.types.audio_config import SpeechModelVariant +from google.cloud.dialogflow_v2.types.audio_config import SpeechToTextConfig from google.cloud.dialogflow_v2.types.audio_config import SpeechWordInfo from google.cloud.dialogflow_v2.types.audio_config import SsmlVoiceGender from google.cloud.dialogflow_v2.types.audio_config import SynthesizeSpeechConfig +from google.cloud.dialogflow_v2.types.audio_config import TelephonyDtmf +from google.cloud.dialogflow_v2.types.audio_config import TelephonyDtmfEvents from google.cloud.dialogflow_v2.types.audio_config import VoiceSelectionParams from google.cloud.dialogflow_v2.types.context import Context from google.cloud.dialogflow_v2.types.context import CreateContextRequest @@ -71,6 +111,59 @@ from google.cloud.dialogflow_v2.types.context import ListContextsRequest from google.cloud.dialogflow_v2.types.context import ListContextsResponse from google.cloud.dialogflow_v2.types.context import UpdateContextRequest +from google.cloud.dialogflow_v2.types.conversation import CallMatcher +from google.cloud.dialogflow_v2.types.conversation import CompleteConversationRequest +from google.cloud.dialogflow_v2.types.conversation import Conversation +from google.cloud.dialogflow_v2.types.conversation import ConversationPhoneNumber +from google.cloud.dialogflow_v2.types.conversation import CreateCallMatcherRequest +from google.cloud.dialogflow_v2.types.conversation import CreateConversationRequest +from google.cloud.dialogflow_v2.types.conversation import DeleteCallMatcherRequest +from google.cloud.dialogflow_v2.types.conversation import GetConversationRequest +from google.cloud.dialogflow_v2.types.conversation import ListCallMatchersRequest +from google.cloud.dialogflow_v2.types.conversation import ListCallMatchersResponse +from google.cloud.dialogflow_v2.types.conversation import ListConversationsRequest +from google.cloud.dialogflow_v2.types.conversation import ListConversationsResponse +from google.cloud.dialogflow_v2.types.conversation import ListMessagesRequest +from google.cloud.dialogflow_v2.types.conversation import ListMessagesResponse +from google.cloud.dialogflow_v2.types.conversation_event import ConversationEvent +from google.cloud.dialogflow_v2.types.conversation_profile import AutomatedAgentConfig +from google.cloud.dialogflow_v2.types.conversation_profile import ConversationProfile +from google.cloud.dialogflow_v2.types.conversation_profile import ( + CreateConversationProfileRequest, +) +from google.cloud.dialogflow_v2.types.conversation_profile import ( + DeleteConversationProfileRequest, +) +from google.cloud.dialogflow_v2.types.conversation_profile import ( + GetConversationProfileRequest, +) +from google.cloud.dialogflow_v2.types.conversation_profile import ( + HumanAgentAssistantConfig, +) +from google.cloud.dialogflow_v2.types.conversation_profile import ( + HumanAgentHandoffConfig, +) +from google.cloud.dialogflow_v2.types.conversation_profile import ( + ListConversationProfilesRequest, +) +from google.cloud.dialogflow_v2.types.conversation_profile import ( + ListConversationProfilesResponse, +) +from google.cloud.dialogflow_v2.types.conversation_profile import LoggingConfig +from google.cloud.dialogflow_v2.types.conversation_profile import NotificationConfig +from google.cloud.dialogflow_v2.types.conversation_profile import SuggestionFeature +from google.cloud.dialogflow_v2.types.conversation_profile import ( + UpdateConversationProfileRequest, +) +from google.cloud.dialogflow_v2.types.document import CreateDocumentRequest +from google.cloud.dialogflow_v2.types.document import DeleteDocumentRequest +from google.cloud.dialogflow_v2.types.document import Document +from google.cloud.dialogflow_v2.types.document import GetDocumentRequest +from google.cloud.dialogflow_v2.types.document import KnowledgeOperationMetadata +from google.cloud.dialogflow_v2.types.document import ListDocumentsRequest +from google.cloud.dialogflow_v2.types.document import ListDocumentsResponse +from google.cloud.dialogflow_v2.types.document import ReloadDocumentRequest +from google.cloud.dialogflow_v2.types.document import UpdateDocumentRequest from google.cloud.dialogflow_v2.types.entity_type import BatchCreateEntitiesRequest from google.cloud.dialogflow_v2.types.entity_type import BatchDeleteEntitiesRequest from google.cloud.dialogflow_v2.types.entity_type import BatchDeleteEntityTypesRequest @@ -88,6 +181,9 @@ from google.cloud.dialogflow_v2.types.environment import Environment from google.cloud.dialogflow_v2.types.environment import ListEnvironmentsRequest from google.cloud.dialogflow_v2.types.environment import ListEnvironmentsResponse +from google.cloud.dialogflow_v2.types.human_agent_assistant_event import ( + HumanAgentAssistantEvent, +) from google.cloud.dialogflow_v2.types.intent import BatchDeleteIntentsRequest from google.cloud.dialogflow_v2.types.intent import BatchUpdateIntentsRequest from google.cloud.dialogflow_v2.types.intent import BatchUpdateIntentsResponse @@ -100,6 +196,38 @@ from google.cloud.dialogflow_v2.types.intent import ListIntentsRequest from google.cloud.dialogflow_v2.types.intent import ListIntentsResponse from google.cloud.dialogflow_v2.types.intent import UpdateIntentRequest +from google.cloud.dialogflow_v2.types.knowledge_base import CreateKnowledgeBaseRequest +from google.cloud.dialogflow_v2.types.knowledge_base import DeleteKnowledgeBaseRequest +from google.cloud.dialogflow_v2.types.knowledge_base import GetKnowledgeBaseRequest +from google.cloud.dialogflow_v2.types.knowledge_base import KnowledgeBase +from google.cloud.dialogflow_v2.types.knowledge_base import ListKnowledgeBasesRequest +from google.cloud.dialogflow_v2.types.knowledge_base import ListKnowledgeBasesResponse +from google.cloud.dialogflow_v2.types.knowledge_base import UpdateKnowledgeBaseRequest +from google.cloud.dialogflow_v2.types.participant import AnalyzeContentRequest +from google.cloud.dialogflow_v2.types.participant import AnalyzeContentResponse +from google.cloud.dialogflow_v2.types.participant import AnnotatedMessagePart +from google.cloud.dialogflow_v2.types.participant import ArticleAnswer +from google.cloud.dialogflow_v2.types.participant import AudioInput +from google.cloud.dialogflow_v2.types.participant import AutomatedAgentReply +from google.cloud.dialogflow_v2.types.participant import CreateParticipantRequest +from google.cloud.dialogflow_v2.types.participant import DtmfParameters +from google.cloud.dialogflow_v2.types.participant import FaqAnswer +from google.cloud.dialogflow_v2.types.participant import GetParticipantRequest +from google.cloud.dialogflow_v2.types.participant import InputTextConfig +from google.cloud.dialogflow_v2.types.participant import ListParticipantsRequest +from google.cloud.dialogflow_v2.types.participant import ListParticipantsResponse +from google.cloud.dialogflow_v2.types.participant import Message +from google.cloud.dialogflow_v2.types.participant import MessageAnnotation +from google.cloud.dialogflow_v2.types.participant import OutputAudio +from google.cloud.dialogflow_v2.types.participant import Participant +from google.cloud.dialogflow_v2.types.participant import StreamingAnalyzeContentRequest +from google.cloud.dialogflow_v2.types.participant import StreamingAnalyzeContentResponse +from google.cloud.dialogflow_v2.types.participant import SuggestArticlesRequest +from google.cloud.dialogflow_v2.types.participant import SuggestArticlesResponse +from google.cloud.dialogflow_v2.types.participant import SuggestFaqAnswersRequest +from google.cloud.dialogflow_v2.types.participant import SuggestFaqAnswersResponse +from google.cloud.dialogflow_v2.types.participant import SuggestionResult +from google.cloud.dialogflow_v2.types.participant import UpdateParticipantRequest from google.cloud.dialogflow_v2.types.session import DetectIntentRequest from google.cloud.dialogflow_v2.types.session import DetectIntentResponse from google.cloud.dialogflow_v2.types.session import EventInput @@ -140,9 +268,22 @@ __all__ = ( "Agent", + "AgentAssistantFeedback", + "AgentAssistantRecord", "AgentsAsyncClient", "AgentsClient", + "AnalyzeContentRequest", + "AnalyzeContentResponse", + "AnnotatedMessagePart", + "AnswerFeedback", + "AnswerRecord", + "AnswerRecordsAsyncClient", + "AnswerRecordsClient", + "ArticleAnswer", "AudioEncoding", + "AudioInput", + "AutomatedAgentConfig", + "AutomatedAgentReply", "BatchCreateEntitiesRequest", "BatchDeleteEntitiesRequest", "BatchDeleteEntityTypesRequest", @@ -152,21 +293,45 @@ "BatchUpdateEntityTypesResponse", "BatchUpdateIntentsRequest", "BatchUpdateIntentsResponse", + "CallMatcher", + "CompleteConversationRequest", "Context", "ContextsAsyncClient", "ContextsClient", + "Conversation", + "ConversationEvent", + "ConversationPhoneNumber", + "ConversationProfile", + "ConversationProfilesAsyncClient", + "ConversationProfilesClient", + "ConversationsAsyncClient", + "ConversationsClient", + "CreateCallMatcherRequest", "CreateContextRequest", + "CreateConversationProfileRequest", + "CreateConversationRequest", + "CreateDocumentRequest", "CreateEntityTypeRequest", "CreateIntentRequest", + "CreateKnowledgeBaseRequest", + "CreateParticipantRequest", "CreateSessionEntityTypeRequest", "DeleteAgentRequest", "DeleteAllContextsRequest", + "DeleteCallMatcherRequest", "DeleteContextRequest", + "DeleteConversationProfileRequest", + "DeleteDocumentRequest", "DeleteEntityTypeRequest", "DeleteIntentRequest", + "DeleteKnowledgeBaseRequest", "DeleteSessionEntityTypeRequest", "DetectIntentRequest", "DetectIntentResponse", + "Document", + "DocumentsAsyncClient", + "DocumentsClient", + "DtmfParameters", "EntityType", "EntityTypeBatch", "EntityTypesAsyncClient", @@ -177,35 +342,74 @@ "EventInput", "ExportAgentRequest", "ExportAgentResponse", + "FaqAnswer", "GetAgentRequest", "GetContextRequest", + "GetConversationProfileRequest", + "GetConversationRequest", + "GetDocumentRequest", "GetEntityTypeRequest", "GetIntentRequest", + "GetKnowledgeBaseRequest", + "GetParticipantRequest", "GetSessionEntityTypeRequest", "GetValidationResultRequest", + "HumanAgentAssistantConfig", + "HumanAgentAssistantEvent", + "HumanAgentHandoffConfig", "ImportAgentRequest", "InputAudioConfig", + "InputTextConfig", "Intent", "IntentBatch", "IntentView", "IntentsAsyncClient", "IntentsClient", + "KnowledgeBase", + "KnowledgeBasesAsyncClient", + "KnowledgeBasesClient", + "KnowledgeOperationMetadata", + "ListAnswerRecordsRequest", + "ListAnswerRecordsResponse", + "ListCallMatchersRequest", + "ListCallMatchersResponse", "ListContextsRequest", "ListContextsResponse", + "ListConversationProfilesRequest", + "ListConversationProfilesResponse", + "ListConversationsRequest", + "ListConversationsResponse", + "ListDocumentsRequest", + "ListDocumentsResponse", "ListEntityTypesRequest", "ListEntityTypesResponse", "ListEnvironmentsRequest", "ListEnvironmentsResponse", "ListIntentsRequest", "ListIntentsResponse", + "ListKnowledgeBasesRequest", + "ListKnowledgeBasesResponse", + "ListMessagesRequest", + "ListMessagesResponse", + "ListParticipantsRequest", + "ListParticipantsResponse", "ListSessionEntityTypesRequest", "ListSessionEntityTypesResponse", + "LoggingConfig", + "Message", + "MessageAnnotation", + "NotificationConfig", "OriginalDetectIntentRequest", + "OutputAudio", "OutputAudioConfig", "OutputAudioEncoding", + "Participant", + "ParticipantsAsyncClient", + "ParticipantsClient", "QueryInput", "QueryParameters", "QueryResult", + "ReloadDocumentRequest", "RestoreAgentRequest", "SearchAgentsRequest", "SearchAgentsResponse", @@ -220,17 +424,33 @@ "SetAgentRequest", "SpeechContext", "SpeechModelVariant", + "SpeechToTextConfig", "SpeechWordInfo", "SsmlVoiceGender", + "StreamingAnalyzeContentRequest", + "StreamingAnalyzeContentResponse", "StreamingDetectIntentRequest", "StreamingDetectIntentResponse", "StreamingRecognitionResult", + "SuggestArticlesRequest", + "SuggestArticlesResponse", + "SuggestFaqAnswersRequest", + "SuggestFaqAnswersResponse", + "SuggestionFeature", + "SuggestionResult", "SynthesizeSpeechConfig", + "TelephonyDtmf", + "TelephonyDtmfEvents", "TextInput", "TrainAgentRequest", + "UpdateAnswerRecordRequest", "UpdateContextRequest", + "UpdateConversationProfileRequest", + "UpdateDocumentRequest", "UpdateEntityTypeRequest", "UpdateIntentRequest", + "UpdateKnowledgeBaseRequest", + "UpdateParticipantRequest", "UpdateSessionEntityTypeRequest", "ValidationError", "ValidationResult", diff --git a/google/cloud/dialogflow_v2/__init__.py b/google/cloud/dialogflow_v2/__init__.py index 0b3372e05..758d431f3 100644 --- a/google/cloud/dialogflow_v2/__init__.py +++ b/google/cloud/dialogflow_v2/__init__.py @@ -16,10 +16,16 @@ # from .services.agents import AgentsClient +from .services.answer_records import AnswerRecordsClient from .services.contexts import ContextsClient +from .services.conversation_profiles import ConversationProfilesClient +from .services.conversations import ConversationsClient +from .services.documents import DocumentsClient from .services.entity_types import EntityTypesClient from .services.environments import EnvironmentsClient from .services.intents import IntentsClient +from .services.knowledge_bases import KnowledgeBasesClient +from .services.participants import ParticipantsClient from .services.session_entity_types import SessionEntityTypesClient from .services.sessions import SessionsClient from .types.agent import Agent @@ -34,15 +40,25 @@ from .types.agent import SearchAgentsResponse from .types.agent import SetAgentRequest from .types.agent import TrainAgentRequest +from .types.answer_record import AgentAssistantFeedback +from .types.answer_record import AgentAssistantRecord +from .types.answer_record import AnswerFeedback +from .types.answer_record import AnswerRecord +from .types.answer_record import ListAnswerRecordsRequest +from .types.answer_record import ListAnswerRecordsResponse +from .types.answer_record import UpdateAnswerRecordRequest 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 SpeechContext from .types.audio_config import SpeechModelVariant +from .types.audio_config import SpeechToTextConfig from .types.audio_config import SpeechWordInfo from .types.audio_config import SsmlVoiceGender from .types.audio_config import SynthesizeSpeechConfig +from .types.audio_config import TelephonyDtmf +from .types.audio_config import TelephonyDtmfEvents from .types.audio_config import VoiceSelectionParams from .types.context import Context from .types.context import CreateContextRequest @@ -52,6 +68,43 @@ from .types.context import ListContextsRequest from .types.context import ListContextsResponse from .types.context import UpdateContextRequest +from .types.conversation import CallMatcher +from .types.conversation import CompleteConversationRequest +from .types.conversation import Conversation +from .types.conversation import ConversationPhoneNumber +from .types.conversation import CreateCallMatcherRequest +from .types.conversation import CreateConversationRequest +from .types.conversation import DeleteCallMatcherRequest +from .types.conversation import GetConversationRequest +from .types.conversation import ListCallMatchersRequest +from .types.conversation import ListCallMatchersResponse +from .types.conversation import ListConversationsRequest +from .types.conversation import ListConversationsResponse +from .types.conversation import ListMessagesRequest +from .types.conversation import ListMessagesResponse +from .types.conversation_event import ConversationEvent +from .types.conversation_profile import AutomatedAgentConfig +from .types.conversation_profile import ConversationProfile +from .types.conversation_profile import CreateConversationProfileRequest +from .types.conversation_profile import DeleteConversationProfileRequest +from .types.conversation_profile import GetConversationProfileRequest +from .types.conversation_profile import HumanAgentAssistantConfig +from .types.conversation_profile import HumanAgentHandoffConfig +from .types.conversation_profile import ListConversationProfilesRequest +from .types.conversation_profile import ListConversationProfilesResponse +from .types.conversation_profile import LoggingConfig +from .types.conversation_profile import NotificationConfig +from .types.conversation_profile import SuggestionFeature +from .types.conversation_profile import UpdateConversationProfileRequest +from .types.document import CreateDocumentRequest +from .types.document import DeleteDocumentRequest +from .types.document import Document +from .types.document import GetDocumentRequest +from .types.document import KnowledgeOperationMetadata +from .types.document import ListDocumentsRequest +from .types.document import ListDocumentsResponse +from .types.document import ReloadDocumentRequest +from .types.document import UpdateDocumentRequest from .types.entity_type import BatchCreateEntitiesRequest from .types.entity_type import BatchDeleteEntitiesRequest from .types.entity_type import BatchDeleteEntityTypesRequest @@ -69,6 +122,7 @@ from .types.environment import Environment from .types.environment import ListEnvironmentsRequest from .types.environment import ListEnvironmentsResponse +from .types.human_agent_assistant_event import HumanAgentAssistantEvent from .types.intent import BatchDeleteIntentsRequest from .types.intent import BatchUpdateIntentsRequest from .types.intent import BatchUpdateIntentsResponse @@ -81,6 +135,38 @@ from .types.intent import ListIntentsRequest from .types.intent import ListIntentsResponse from .types.intent import UpdateIntentRequest +from .types.knowledge_base import CreateKnowledgeBaseRequest +from .types.knowledge_base import DeleteKnowledgeBaseRequest +from .types.knowledge_base import GetKnowledgeBaseRequest +from .types.knowledge_base import KnowledgeBase +from .types.knowledge_base import ListKnowledgeBasesRequest +from .types.knowledge_base import ListKnowledgeBasesResponse +from .types.knowledge_base import UpdateKnowledgeBaseRequest +from .types.participant import AnalyzeContentRequest +from .types.participant import AnalyzeContentResponse +from .types.participant import AnnotatedMessagePart +from .types.participant import ArticleAnswer +from .types.participant import AudioInput +from .types.participant import AutomatedAgentReply +from .types.participant import CreateParticipantRequest +from .types.participant import DtmfParameters +from .types.participant import FaqAnswer +from .types.participant import GetParticipantRequest +from .types.participant import InputTextConfig +from .types.participant import ListParticipantsRequest +from .types.participant import ListParticipantsResponse +from .types.participant import Message +from .types.participant import MessageAnnotation +from .types.participant import OutputAudio +from .types.participant import Participant +from .types.participant import StreamingAnalyzeContentRequest +from .types.participant import StreamingAnalyzeContentResponse +from .types.participant import SuggestArticlesRequest +from .types.participant import SuggestArticlesResponse +from .types.participant import SuggestFaqAnswersRequest +from .types.participant import SuggestFaqAnswersResponse +from .types.participant import SuggestionResult +from .types.participant import UpdateParticipantRequest from .types.session import DetectIntentRequest from .types.session import DetectIntentResponse from .types.session import EventInput @@ -110,8 +196,20 @@ __all__ = ( "Agent", + "AgentAssistantFeedback", + "AgentAssistantRecord", "AgentsClient", + "AnalyzeContentRequest", + "AnalyzeContentResponse", + "AnnotatedMessagePart", + "AnswerFeedback", + "AnswerRecord", + "AnswerRecordsClient", + "ArticleAnswer", "AudioEncoding", + "AudioInput", + "AutomatedAgentConfig", + "AutomatedAgentReply", "BatchCreateEntitiesRequest", "BatchDeleteEntitiesRequest", "BatchDeleteEntityTypesRequest", @@ -121,20 +219,41 @@ "BatchUpdateEntityTypesResponse", "BatchUpdateIntentsRequest", "BatchUpdateIntentsResponse", + "CallMatcher", + "CompleteConversationRequest", "Context", "ContextsClient", + "Conversation", + "ConversationEvent", + "ConversationPhoneNumber", + "ConversationProfile", + "ConversationProfilesClient", + "ConversationsClient", + "CreateCallMatcherRequest", "CreateContextRequest", + "CreateConversationProfileRequest", + "CreateConversationRequest", + "CreateDocumentRequest", "CreateEntityTypeRequest", "CreateIntentRequest", + "CreateKnowledgeBaseRequest", + "CreateParticipantRequest", "CreateSessionEntityTypeRequest", "DeleteAgentRequest", "DeleteAllContextsRequest", + "DeleteCallMatcherRequest", "DeleteContextRequest", + "DeleteConversationProfileRequest", + "DeleteDocumentRequest", "DeleteEntityTypeRequest", "DeleteIntentRequest", + "DeleteKnowledgeBaseRequest", "DeleteSessionEntityTypeRequest", "DetectIntentRequest", "DetectIntentResponse", + "Document", + "DocumentsClient", + "DtmfParameters", "EntityType", "EntityTypeBatch", "EntityTypesClient", @@ -143,33 +262,70 @@ "EventInput", "ExportAgentRequest", "ExportAgentResponse", + "FaqAnswer", "GetAgentRequest", "GetContextRequest", + "GetConversationProfileRequest", + "GetConversationRequest", + "GetDocumentRequest", "GetEntityTypeRequest", "GetIntentRequest", + "GetKnowledgeBaseRequest", + "GetParticipantRequest", "GetSessionEntityTypeRequest", "GetValidationResultRequest", + "HumanAgentAssistantConfig", + "HumanAgentAssistantEvent", + "HumanAgentHandoffConfig", "ImportAgentRequest", "InputAudioConfig", + "InputTextConfig", "Intent", "IntentBatch", "IntentView", + "IntentsClient", + "KnowledgeBase", + "KnowledgeOperationMetadata", + "ListAnswerRecordsRequest", + "ListAnswerRecordsResponse", + "ListCallMatchersRequest", + "ListCallMatchersResponse", "ListContextsRequest", "ListContextsResponse", + "ListConversationProfilesRequest", + "ListConversationProfilesResponse", + "ListConversationsRequest", + "ListConversationsResponse", + "ListDocumentsRequest", + "ListDocumentsResponse", "ListEntityTypesRequest", "ListEntityTypesResponse", "ListEnvironmentsRequest", "ListEnvironmentsResponse", "ListIntentsRequest", "ListIntentsResponse", + "ListKnowledgeBasesRequest", + "ListKnowledgeBasesResponse", + "ListMessagesRequest", + "ListMessagesResponse", + "ListParticipantsRequest", + "ListParticipantsResponse", "ListSessionEntityTypesRequest", "ListSessionEntityTypesResponse", + "LoggingConfig", + "Message", + "MessageAnnotation", + "NotificationConfig", "OriginalDetectIntentRequest", + "OutputAudio", "OutputAudioConfig", "OutputAudioEncoding", + "Participant", + "ParticipantsClient", "QueryInput", "QueryParameters", "QueryResult", + "ReloadDocumentRequest", "RestoreAgentRequest", "SearchAgentsRequest", "SearchAgentsResponse", @@ -182,22 +338,38 @@ "SetAgentRequest", "SpeechContext", "SpeechModelVariant", + "SpeechToTextConfig", "SpeechWordInfo", "SsmlVoiceGender", + "StreamingAnalyzeContentRequest", + "StreamingAnalyzeContentResponse", "StreamingDetectIntentRequest", "StreamingDetectIntentResponse", "StreamingRecognitionResult", + "SuggestArticlesRequest", + "SuggestArticlesResponse", + "SuggestFaqAnswersRequest", + "SuggestFaqAnswersResponse", + "SuggestionFeature", + "SuggestionResult", "SynthesizeSpeechConfig", + "TelephonyDtmf", + "TelephonyDtmfEvents", "TextInput", "TrainAgentRequest", + "UpdateAnswerRecordRequest", "UpdateContextRequest", + "UpdateConversationProfileRequest", + "UpdateDocumentRequest", "UpdateEntityTypeRequest", "UpdateIntentRequest", + "UpdateKnowledgeBaseRequest", + "UpdateParticipantRequest", "UpdateSessionEntityTypeRequest", "ValidationError", "ValidationResult", "VoiceSelectionParams", "WebhookRequest", "WebhookResponse", - "IntentsClient", + "KnowledgeBasesClient", ) diff --git a/google/cloud/dialogflow_v2/proto/agent.proto b/google/cloud/dialogflow_v2/proto/agent.proto index 82c1426c7..4ac05cd2a 100644 --- a/google/cloud/dialogflow_v2/proto/agent.proto +++ b/google/cloud/dialogflow_v2/proto/agent.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/google/cloud/dialogflow_v2/proto/answer_record.proto b/google/cloud/dialogflow_v2/proto/answer_record.proto new file mode 100644 index 000000000..1afb47e63 --- /dev/null +++ b/google/cloud/dialogflow_v2/proto/answer_record.proto @@ -0,0 +1,297 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/cloud/dialogflow/v2/participant.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "AnswerRecordsProto"; +option java_package = "com.google.cloud.dialogflow.v2"; +option objc_class_prefix = "DF"; + +// Service for managing [AnswerRecords][google.cloud.dialogflow.v2.AnswerRecord]. +service AnswerRecords { + option (google.api.default_host) = "dialogflow.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/dialogflow"; + + // Returns the list of all answer records in the specified project in reverse + // chronological order. + rpc ListAnswerRecords(ListAnswerRecordsRequest) returns (ListAnswerRecordsResponse) { + option (google.api.http) = { + get: "/v2/{parent=projects/*}/answerRecords" + additional_bindings { + get: "/v2/{parent=projects/*/locations/*}/answerRecords" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Updates the specified answer record. + rpc UpdateAnswerRecord(UpdateAnswerRecordRequest) returns (AnswerRecord) { + option (google.api.http) = { + patch: "/v2/{answer_record.name=projects/*/answerRecords/*}" + body: "answer_record" + additional_bindings { + patch: "/v2/{answer_record.name=projects/*/locations/*/answerRecords/*}" + body: "answer_record" + } + }; + option (google.api.method_signature) = "answer_record,update_mask"; + } +} + +// Answer records are records to manage answer history and feedbacks for +// Dialogflow. +// +// Currently, answer record includes: +// +// - human agent assistant article suggestion +// - human agent assistant faq article +// +// It doesn't include: +// +// - `DetectIntent` intent matching +// - `DetectIntent` knowledge +// +// Answer records are not related to the conversation history in the +// Dialogflow Console. A Record is generated even when the end-user disables +// conversation history in the console. Records are created when there's a human +// agent assistant suggestion generated. +// +// A typical workflow for customers provide feedback to an answer is: +// +// 1. For human agent assistant, customers get suggestion via ListSuggestions +// API. Together with the answers, [AnswerRecord.name][google.cloud.dialogflow.v2.AnswerRecord.name] are returned to the +// customers. +// 2. The customer uses the [AnswerRecord.name][google.cloud.dialogflow.v2.AnswerRecord.name] to call the +// [UpdateAnswerRecord][] method to send feedback about a specific answer +// that they believe is wrong. +message AnswerRecord { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/AnswerRecord" + pattern: "projects/{project}/answerRecords/{answer_record}" + pattern: "projects/{project}/locations/{location}/answerRecords/{answer_record}" + }; + + // The unique identifier of this answer record. + // Format: `projects//locations//answerRecords/`. + string name = 1; + + // Required. The AnswerFeedback for this record. You can set this with + // [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2.AnswerRecords.UpdateAnswerRecord] in order to give us feedback about + // this answer. + AnswerFeedback answer_feedback = 2 [(google.api.field_behavior) = REQUIRED]; + + // The record for this answer. + oneof record { + // Output only. The record for human agent assistant. + AgentAssistantRecord agent_assistant_record = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; + } +} + +// Request message for [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2.AnswerRecords.ListAnswerRecords]. +message ListAnswerRecordsRequest { + // Required. The project to list all answer records for in reverse + // chronological order. Format: `projects//locations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/AnswerRecord" + } + ]; + + // Required. Filters to restrict results to specific answer records. + // Filter on answer record type. Currently predicates on `type` is supported, + // valid values are `ARTICLE_ANSWER`, `FAQ_ANSWER`. + // + // For more information about filtering, see + // [API Filtering](https://aip.dev/160). + string filter = 2 [(google.api.field_behavior) = REQUIRED]; + + // Optional. The maximum number of records to return in a single page. + // The server may return fewer records than this. If unspecified, we use 10. + // The maximum is 100. + int32 page_size = 3 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The + // [ListAnswerRecordsResponse.next_page_token][google.cloud.dialogflow.v2.ListAnswerRecordsResponse.next_page_token] + // value returned from a previous list request used to continue listing on + // the next page. + string page_token = 4 [(google.api.field_behavior) = OPTIONAL]; +} + +// Response message for [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2.AnswerRecords.ListAnswerRecords]. +message ListAnswerRecordsResponse { + // The list of answer records. + repeated AnswerRecord answer_records = 1; + + // A token to retrieve next page of results. Or empty if there are no more + // results. + // Pass this value in the + // [ListAnswerRecordsRequest.page_token][google.cloud.dialogflow.v2.ListAnswerRecordsRequest.page_token] + // field in the subsequent call to `ListAnswerRecords` method to retrieve the + // next page of results. + string next_page_token = 2; +} + +// Request message for [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2.AnswerRecords.UpdateAnswerRecord]. +message UpdateAnswerRecordRequest { + // Required. Answer record to update. + AnswerRecord answer_record = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/AnswerRecord" + } + ]; + + // Required. The mask to control which fields get updated. + google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// Represents feedback the customer has about the quality & correctness of a +// certain answer in a conversation. +message AnswerFeedback { + // The correctness level of an answer. + enum CorrectnessLevel { + // Correctness level unspecified. + CORRECTNESS_LEVEL_UNSPECIFIED = 0; + + // Answer is totally wrong. + NOT_CORRECT = 1; + + // Answer is partially correct. + PARTIALLY_CORRECT = 2; + + // Answer is fully correct. + FULLY_CORRECT = 3; + } + + // The correctness level of the specific answer. + CorrectnessLevel correctness_level = 1; + + // Normally, detail feedback is provided when answer is not fully correct. + oneof detail_feedback { + // Detail feedback of agent assist suggestions. + AgentAssistantFeedback agent_assistant_detail_feedback = 2; + } + + // Indicates whether the answer/item was clicked by the human agent + // or not. Default to false. + bool clicked = 3; + + // Time when the answer/item was clicked. + google.protobuf.Timestamp click_time = 5; + + // Indicates whether the answer/item was displayed to the human + // agent in the agent desktop UI. Default to false. + bool displayed = 4; + + // Time when the answer/item was displayed. + google.protobuf.Timestamp display_time = 6; +} + +// Detail feedback of Agent Assist result. +message AgentAssistantFeedback { + // Relevance of an answer. + enum AnswerRelevance { + // Answer relevance unspecified. + ANSWER_RELEVANCE_UNSPECIFIED = 0; + + // Answer is irrelevant to query. + IRRELEVANT = 1; + + // Answer is relevant to query. + RELEVANT = 2; + } + + // Correctness of document. + enum DocumentCorrectness { + // Document correctness unspecified. + DOCUMENT_CORRECTNESS_UNSPECIFIED = 0; + + // Information in document is incorrect. + INCORRECT = 1; + + // Information in document is correct. + CORRECT = 2; + } + + // Efficiency of document. + enum DocumentEfficiency { + // Document efficiency unspecified. + DOCUMENT_EFFICIENCY_UNSPECIFIED = 0; + + // Document is inefficient. + INEFFICIENT = 1; + + // Document is efficient. + EFFICIENT = 2; + } + + // Optional. Whether or not the suggested answer is relevant. + // + // For example: + // + // * Query: "Can I change my mailing address?" + // * Suggested document says: "Items must be returned/exchanged within 60 + // days of the purchase date." + // * [answer_relevance][google.cloud.dialogflow.v2.AgentAssistantFeedback.answer_relevance]: [AnswerRelevance.IRRELEVANT][google.cloud.dialogflow.v2.AgentAssistantFeedback.AnswerRelevance.IRRELEVANT] + AnswerRelevance answer_relevance = 1 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Whether or not the information in the document is correct. + // + // For example: + // + // * Query: "Can I return the package in 2 days once received?" + // * Suggested document says: "Items must be returned/exchanged within 60 + // days of the purchase date." + // * Ground truth: "No return or exchange is allowed." + // * [document_correctness]: INCORRECT + DocumentCorrectness document_correctness = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Whether or not the suggested document is efficient. For example, + // if the document is poorly written, hard to understand, hard to use or + // too long to find useful information, [document_efficiency][google.cloud.dialogflow.v2.AgentAssistantFeedback.document_efficiency] is + // [DocumentEfficiency.INEFFICIENT][google.cloud.dialogflow.v2.AgentAssistantFeedback.DocumentEfficiency.INEFFICIENT]. + DocumentEfficiency document_efficiency = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// Represents a record of a human agent assist answer. +message AgentAssistantRecord { + // Output only. The agent assist answer. + oneof answer { + // Output only. The article suggestion answer. + ArticleAnswer article_suggestion_answer = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The FAQ answer. + FaqAnswer faq_answer = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; + } +} diff --git a/google/cloud/dialogflow_v2/proto/audio_config.proto b/google/cloud/dialogflow_v2/proto/audio_config.proto index dcfd650fc..674535b4b 100644 --- a/google/cloud/dialogflow_v2/proto/audio_config.proto +++ b/google/cloud/dialogflow_v2/proto/audio_config.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -247,6 +247,12 @@ message InputAudioConfig { // Note: When specified, InputAudioConfig.single_utterance takes precedence // over StreamingDetectIntentRequest.single_utterance. bool single_utterance = 8; + + // Only used in [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent] and + // [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2.Participants.StreamingAnalyzeContent]. + // If `false` and recognition doesn't return any result, trigger + // `NO_SPEECH_RECOGNIZED` event to Dialogflow agent. + bool disable_no_speech_recognized_event = 14; } // Description of which voice to use for speech synthesis. @@ -264,23 +270,6 @@ message VoiceSelectionParams { SsmlVoiceGender ssml_gender = 2; } -// Gender of the voice as described in -// [SSML voice element](https://www.w3.org/TR/speech-synthesis11/#edef_voice). -enum SsmlVoiceGender { - // An unspecified gender, which means that the client doesn't care which - // gender the selected voice will have. - SSML_VOICE_GENDER_UNSPECIFIED = 0; - - // A male voice. - SSML_VOICE_GENDER_MALE = 1; - - // A female voice. - SSML_VOICE_GENDER_FEMALE = 2; - - // A gender-neutral voice. - SSML_VOICE_GENDER_NEUTRAL = 3; -} - // Configuration of how speech should be synthesized. message SynthesizeSpeechConfig { // Optional. Speaking rate/speed, in the range [0.25, 4.0]. 1.0 is the normal @@ -313,6 +302,23 @@ message SynthesizeSpeechConfig { VoiceSelectionParams voice = 4; } +// Gender of the voice as described in +// [SSML voice element](https://www.w3.org/TR/speech-synthesis11/#edef_voice). +enum SsmlVoiceGender { + // An unspecified gender, which means that the client doesn't care which + // gender the selected voice will have. + SSML_VOICE_GENDER_UNSPECIFIED = 0; + + // A male voice. + SSML_VOICE_GENDER_MALE = 1; + + // A female voice. + SSML_VOICE_GENDER_FEMALE = 2; + + // A gender-neutral voice. + SSML_VOICE_GENDER_NEUTRAL = 3; +} + // Instructs the speech synthesizer on how to generate the output audio content. // If this audio config is supplied in a request, it overrides all existing // text-to-speech settings applied to the agent. @@ -331,6 +337,12 @@ message OutputAudioConfig { SynthesizeSpeechConfig synthesize_speech_config = 3; } +// A wrapper of repeated TelephonyDtmf digits. +message TelephonyDtmfEvents { + // A sequence of TelephonyDtmf digits. + repeated TelephonyDtmf dtmf_events = 1; +} + // Audio encoding of the output audio format in Text-To-Speech. enum OutputAudioEncoding { // Not specified. @@ -349,3 +361,67 @@ enum OutputAudioEncoding { // than MP3 while using approximately the same bitrate. OUTPUT_AUDIO_ENCODING_OGG_OPUS = 3; } + +// Configures speech transcription for [ConversationProfile][google.cloud.dialogflow.v2.ConversationProfile]. +message SpeechToTextConfig { + // Optional. The speech model used in speech to text. + // `SPEECH_MODEL_VARIANT_UNSPECIFIED`, `USE_BEST_AVAILABLE` will be treated as + // `USE_ENHANCED`. It can be overridden in [AnalyzeContentRequest][google.cloud.dialogflow.v2.AnalyzeContentRequest] and + // [StreamingAnalyzeContentRequest][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest] request. + SpeechModelVariant speech_model_variant = 1 [(google.api.field_behavior) = OPTIONAL]; +} + +// [DTMF](https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling) +// digit in Telephony Gateway. +enum TelephonyDtmf { + // Not specified. This value may be used to indicate an absent digit. + TELEPHONY_DTMF_UNSPECIFIED = 0; + + // Number: '1'. + DTMF_ONE = 1; + + // Number: '2'. + DTMF_TWO = 2; + + // Number: '3'. + DTMF_THREE = 3; + + // Number: '4'. + DTMF_FOUR = 4; + + // Number: '5'. + DTMF_FIVE = 5; + + // Number: '6'. + DTMF_SIX = 6; + + // Number: '7'. + DTMF_SEVEN = 7; + + // Number: '8'. + DTMF_EIGHT = 8; + + // Number: '9'. + DTMF_NINE = 9; + + // Number: '0'. + DTMF_ZERO = 10; + + // Letter: 'A'. + DTMF_A = 11; + + // Letter: 'B'. + DTMF_B = 12; + + // Letter: 'C'. + DTMF_C = 13; + + // Letter: 'D'. + DTMF_D = 14; + + // Asterisk/star: '*'. + DTMF_STAR = 15; + + // Pound/diamond/hash/square/gate/octothorpe: '#'. + DTMF_POUND = 16; +} diff --git a/google/cloud/dialogflow_v2/proto/context.proto b/google/cloud/dialogflow_v2/proto/context.proto index 07740d1f7..246ee7be4 100644 --- a/google/cloud/dialogflow_v2/proto/context.proto +++ b/google/cloud/dialogflow_v2/proto/context.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -169,7 +169,8 @@ message Context { // - 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 + // - Else: depending on parameter value type, could be one of string, + // number, boolean, null, list or map // - MapValue value: // - If parameter's entity type is a composite entity: // map from composite entity property names to property values diff --git a/google/cloud/dialogflow_v2/proto/conversation.proto b/google/cloud/dialogflow_v2/proto/conversation.proto new file mode 100644 index 000000000..5124402cd --- /dev/null +++ b/google/cloud/dialogflow_v2/proto/conversation.proto @@ -0,0 +1,520 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/cloud/dialogflow/v2/participant.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "ConversationProto"; +option java_package = "com.google.cloud.dialogflow.v2"; +option objc_class_prefix = "DF"; + +// Service for managing [Conversations][google.cloud.dialogflow.v2.Conversation]. +service Conversations { + option (google.api.default_host) = "dialogflow.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/dialogflow"; + + // Creates a new conversation. Conversations are auto-completed after 24 + // hours. + // + // Conversation Lifecycle: + // There are two stages during a conversation: Automated Agent Stage and + // Assist Stage. + // + // For Automated Agent Stage, there will be a dialogflow agent responding to + // user queries. + // + // For Assist Stage, there's no dialogflow agent responding to user queries. + // But we will provide suggestions which are generated from conversation. + // + // If [Conversation.conversation_profile][google.cloud.dialogflow.v2.Conversation.conversation_profile] is configured for a dialogflow + // agent, conversation will start from `Automated Agent Stage`, otherwise, it + // will start from `Assist Stage`. And during `Automated Agent Stage`, once an + // [Intent][google.cloud.dialogflow.v2.Intent] with [Intent.live_agent_handoff][google.cloud.dialogflow.v2.Intent.live_agent_handoff] is triggered, conversation + // will transfer to Assist Stage. + rpc CreateConversation(CreateConversationRequest) returns (Conversation) { + option (google.api.http) = { + post: "/v2/{parent=projects/*}/conversations" + body: "conversation" + additional_bindings { + post: "/v2/{parent=projects/*/locations/*}/conversations" + body: "conversation" + } + }; + option (google.api.method_signature) = "parent,conversation"; + } + + // Returns the list of all conversations in the specified project. + rpc ListConversations(ListConversationsRequest) returns (ListConversationsResponse) { + option (google.api.http) = { + get: "/v2/{parent=projects/*}/conversations" + additional_bindings { + get: "/v2/{parent=projects/*/locations/*}/conversations" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Retrieves the specific conversation. + rpc GetConversation(GetConversationRequest) returns (Conversation) { + option (google.api.http) = { + get: "/v2/{name=projects/*/conversations/*}" + additional_bindings { + get: "/v2/{name=projects/*/locations/*/conversations/*}" + } + }; + option (google.api.method_signature) = "name"; + } + + // Completes the specified conversation. Finished conversations are purged + // from the database after 30 days. + rpc CompleteConversation(CompleteConversationRequest) returns (Conversation) { + option (google.api.http) = { + post: "/v2/{name=projects/*/conversations/*}:complete" + body: "*" + additional_bindings { + post: "/v2/{name=projects/*/locations/*/conversations/*}:complete" + body: "*" + } + }; + option (google.api.method_signature) = "name"; + } + + // Creates a call matcher that links incoming SIP calls to the specified + // conversation if they fulfill specified criteria. + rpc CreateCallMatcher(CreateCallMatcherRequest) returns (CallMatcher) { + option (google.api.http) = { + post: "/v2/{parent=projects/*/conversations/*}/callMatchers" + body: "call_matcher" + additional_bindings { + post: "/v2/{parent=projects/*/locations/*/conversations/*}/callMatchers" + body: "*" + } + }; + option (google.api.method_signature) = "parent,call_matcher"; + } + + // Returns the list of all call matchers in the specified conversation. + rpc ListCallMatchers(ListCallMatchersRequest) returns (ListCallMatchersResponse) { + option (google.api.http) = { + get: "/v2/{parent=projects/*/conversations/*}/callMatchers" + additional_bindings { + get: "/v2/{parent=projects/*/locations/*/conversations/*}/callMatchers" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Requests deletion of a call matcher. + rpc DeleteCallMatcher(DeleteCallMatcherRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v2/{name=projects/*/conversations/*/callMatchers/*}" + additional_bindings { + delete: "/v2/{name=projects/*/locations/*/conversations/*/callMatchers/*}" + } + }; + option (google.api.method_signature) = "name"; + } + + // Lists messages that belong to a given conversation. + // `messages` are ordered by `create_time` in descending order. To fetch + // updates without duplication, send request with filter + // `create_time_epoch_microseconds > + // [first item's create_time of previous request]` and empty page_token. + rpc ListMessages(ListMessagesRequest) returns (ListMessagesResponse) { + option (google.api.http) = { + get: "/v2/{parent=projects/*/conversations/*}/messages" + additional_bindings { + get: "/v2/{parent=projects/*/locations/*/conversations/*}/messages" + } + }; + option (google.api.method_signature) = "parent"; + } +} + +// Represents a conversation. +// A conversation is an interaction between an agent, including live agents +// and Dialogflow agents, and a support customer. Conversations can +// include phone calls and text-based chat sessions. +message Conversation { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/Conversation" + pattern: "projects/{project}/conversations/{conversation}" + pattern: "projects/{project}/locations/{location}/conversations/{conversation}" + }; + + // Enumeration of the completion status of the conversation. + enum LifecycleState { + // Unknown. + LIFECYCLE_STATE_UNSPECIFIED = 0; + + // Conversation is currently open for media analysis. + IN_PROGRESS = 1; + + // Conversation has been completed. + COMPLETED = 2; + } + + // Enumeration of the different conversation stages a conversation can be in. + // Reference: + // https://cloud.google.com/dialogflow/priv/docs/contact-center/basics#stages + enum ConversationStage { + // Unknown. Should never be used after a conversation is successfully + // created. + CONVERSATION_STAGE_UNSPECIFIED = 0; + + // The conversation should return virtual agent responses into the + // conversation. + VIRTUAL_AGENT_STAGE = 1; + + // The conversation should not provide responses, just listen and provide + // suggestions. + HUMAN_ASSIST_STAGE = 2; + } + + // Output only. The unique identifier of this conversation. + // Format: `projects//locations//conversations/`. + string name = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The current state of the Conversation. + LifecycleState lifecycle_state = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Required. The Conversation Profile to be used to configure this + // Conversation. This field cannot be updated. + // Format: `projects//locations//conversationProfiles/`. + string conversation_profile = 3 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/ConversationProfile" + } + ]; + + // Output only. It will not be empty if the conversation is to be connected over + // telephony. + ConversationPhoneNumber phone_number = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The time the conversation was started. + google.protobuf.Timestamp start_time = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The time the conversation was finished. + google.protobuf.Timestamp end_time = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // The stage of a conversation. It indicates whether the virtual agent or a + // human agent is handling the conversation. + // + // If the conversation is created with the conversation profile that has + // Dialogflow config set, defaults to + // [ConversationStage.VIRTUAL_AGENT_STAGE][google.cloud.dialogflow.v2.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE]; Otherwise, defaults to + // [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2.Conversation.ConversationStage.HUMAN_ASSIST_STAGE]. + // + // If the conversation is created with the conversation profile that has + // Dialogflow config set but explicitly sets conversation_stage to + // [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2.Conversation.ConversationStage.HUMAN_ASSIST_STAGE], it skips + // [ConversationStage.VIRTUAL_AGENT_STAGE][google.cloud.dialogflow.v2.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE] stage and directly goes to + // [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2.Conversation.ConversationStage.HUMAN_ASSIST_STAGE]. + ConversationStage conversation_stage = 7; +} + +// Represents a call matcher that describes criteria for matching incoming SIP +// calls to a conversation. When Dialogflow get a SIP call from a third-party +// carrier, Dialogflow matches the call to an existing conversation by either: +// +// * Extracting the conversation id from the +// [Call-Info header](https://tools.ietf.org/html/rfc3261#section-20.9), e.g. +// `Call-Info: +// +// ;purpose=Goog-ContactCenter-Conversation`. +// * Or, if that doesn't work, matching incoming [SIP +// headers](https://tools.ietf.org/html/rfc3261#section-7.3) +// against any [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] for the conversation. +// +// If an incoming SIP call without valid `Call-Info` header matches to zero or +// multiple conversations with `CallMatcher`, we reject it. +// +// A call matcher contains equality conditions for SIP headers that all have +// to be fulfilled in order for a SIP call to match. +// +// The matched SIP headers consist of well-known headers (`To`, `From`, +// `Call-ID`) and custom headers. A [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] is only valid if it +// specifies: +// +// * At least 1 custom header, +// * or at least 2 well-known headers. +message CallMatcher { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/CallMatcher" + pattern: "projects/{project}/conversations/{conversation}/callMatchers/{call_matcher}" + pattern: "projects/{project}/locations/{location}/conversations/{conversation}/callMatchers/{call_matcher}" + }; + + // Custom SIP headers. See the [description of headers in + // the RFC](https://tools.ietf.org/html/rfc3261#section-7.3). + message CustomHeaders { + // Cisco's proprietary `Cisco-Guid` header. + string cisco_guid = 1; + } + + // Output only. The unique identifier of this call matcher. + // Format: `projects//locations//conversations//callMatchers/`. + string name = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Value of the [`To` + // header](https://tools.ietf.org/html/rfc3261#section-8.1.1.2) to match. If + // empty or unspecified, we don't match to the + // [`To` header](https://tools.ietf.org/html/rfc3261#section-8.1.1.2). + string to_header = 2; + + // Value of the [`From` + // header](https://tools.ietf.org/html/rfc3261#section-8.1.1.3) to match. If + // empty or unspecified, we don't match to the + // [`From` header](https://tools.ietf.org/html/rfc3261#section-8.1.1.3). + string from_header = 3; + + // Value of the [`Call-ID` + // header](https://tools.ietf.org/html/rfc3261#section-8.1.1.4) to match. If + // empty or unspecified, we don't match to the + // [`Call-ID` header](https://tools.ietf.org/html/rfc3261#section-8.1.1.4). + string call_id_header = 4; + + // Custom SIP headers that must match. + CustomHeaders custom_headers = 5; +} + +// The request message for [Conversations.CreateConversation][google.cloud.dialogflow.v2.Conversations.CreateConversation]. +message CreateConversationRequest { + // Required. Resource identifier of the project creating the conversation. + // Format: `projects//locations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Conversation" + } + ]; + + // Required. The conversation to create. + Conversation conversation = 2 [(google.api.field_behavior) = REQUIRED]; + + // Optional. Identifier of the conversation. Generally it's auto generated by Google. + // Only set it if you cannot wait for the response to return a + // auto-generated one to you. + // + // The conversation ID must be compliant with the regression fomula + // "[a-zA-Z][a-zA-Z0-9_-]*" with the characters length in range of [3,64]. + // If the field is provided, the caller is resposible for + // 1. the uniqueness of the ID, otherwise the request will be rejected. + // 2. the consistency for whether to use custom ID or not under a project to + // better ensure uniqueness. + string conversation_id = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// The request message for [Conversations.ListConversations][google.cloud.dialogflow.v2.Conversations.ListConversations]. +message ListConversationsRequest { + // Required. The project from which to list all conversation. + // Format: `projects//locations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Conversation" + } + ]; + + // Optional. The maximum number of items to return in a single page. By + // default 100 and at most 1000. + int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The next_page_token value returned from a previous list request. + string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; + + // A filter expression that filters conversations listed in the response. In + // general, the expression must specify the field name, a comparison operator, + // and the value to use for filtering: + //
    + //
  • The value must be a string, a number, or a boolean.
  • + //
  • The comparison operator must be either `=`,`!=`, `>`, or `<`.
  • + //
  • To filter on multiple expressions, separate the + // expressions with `AND` or `OR` (omitting both implies `AND`).
  • + //
  • For clarity, expressions can be enclosed in parentheses.
  • + //
+ // Only `lifecycle_state` can be filtered on in this way. For example, + // the following expression only returns `COMPLETED` conversations: + // + // `lifecycle_state = "COMPLETED"` + // + // For more information about filtering, see + // [API Filtering](https://aip.dev/160). + string filter = 4; +} + +// The response message for [Conversations.ListConversations][google.cloud.dialogflow.v2.Conversations.ListConversations]. +message ListConversationsResponse { + // The list of conversations. There will be a maximum number of items + // returned based on the page_size field in the request. + repeated Conversation conversations = 1; + + // Token to retrieve the next page of results, or empty if there are no + // more results in the list. + string next_page_token = 2; +} + +// The request message for [Conversations.GetConversation][google.cloud.dialogflow.v2.Conversations.GetConversation]. +message GetConversationRequest { + // Required. The name of the conversation. Format: + // `projects//locations//conversations/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Conversation" + } + ]; +} + +// The request message for [Conversations.CompleteConversation][google.cloud.dialogflow.v2.Conversations.CompleteConversation]. +message CompleteConversationRequest { + // Required. Resource identifier of the conversation to close. + // Format: `projects//locations//conversations/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Conversation" + } + ]; +} + +// The request message for [Conversations.CreateCallMatcher][google.cloud.dialogflow.v2.Conversations.CreateCallMatcher]. +message CreateCallMatcherRequest { + // Required. Resource identifier of the conversation adding the call matcher. + // Format: `projects//locations//conversations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/CallMatcher" + } + ]; + + // Required. The call matcher to create. + CallMatcher call_matcher = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request message for [Conversations.ListCallMatchers][google.cloud.dialogflow.v2.Conversations.ListCallMatchers]. +message ListCallMatchersRequest { + // Required. The conversation to list all call matchers from. + // Format: `projects//locations//conversations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/CallMatcher" + } + ]; + + // Optional. The maximum number of items to return in a single page. By + // default 100 and at most 1000. + int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The next_page_token value returned from a previous list request. + string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// The response message for [Conversations.ListCallMatchers][google.cloud.dialogflow.v2.Conversations.ListCallMatchers]. +message ListCallMatchersResponse { + // The list of call matchers. There is a maximum number of items + // returned based on the page_size field in the request. + repeated CallMatcher call_matchers = 1; + + // Token to retrieve the next page of results or empty if there are no + // more results in the list. + string next_page_token = 2; +} + +// The request message for [Conversations.DeleteCallMatcher][google.cloud.dialogflow.v2.Conversations.DeleteCallMatcher]. +message DeleteCallMatcherRequest { + // Required. The unique identifier of the [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] to delete. + // Format: `projects//locations//conversations//callMatchers/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/CallMatcher" + } + ]; +} + +// The request message for [Conversations.ListMessages][google.cloud.dialogflow.v2.Conversations.ListMessages]. +message ListMessagesRequest { + // Required. The name of the conversation to list messages for. + // Format: `projects//locations//conversations/` + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Message" + } + ]; + + // Optional. Filter on message fields. Currently predicates on `create_time` + // and `create_time_epoch_microseconds` are supported. `create_time` only + // support milliseconds accuracy. E.g., + // `create_time_epoch_microseconds > 1551790877964485` or + // `create_time > 2017-01-15T01:30:15.01Z`. + // + // For more information about filtering, see + // [API Filtering](https://aip.dev/160). + string filter = 4 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The maximum number of items to return in a single page. By + // default 100 and at most 1000. + int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The next_page_token value returned from a previous list request. + string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// The response message for [Conversations.ListMessages][google.cloud.dialogflow.v2.Conversations.ListMessages]. +message ListMessagesResponse { + // The list of messages. There will be a maximum number of items + // returned based on the page_size field in the request. + // `messages` is sorted by `create_time` in descending order. + repeated Message messages = 1; + + // Token to retrieve the next page of results, or empty if there are + // no more results in the list. + string next_page_token = 2; +} + +// Represents a phone number for telephony integration. It allows for connecting +// a particular conversation over telephony. +message ConversationPhoneNumber { + // Output only. The phone number to connect to this conversation. + string phone_number = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; +} diff --git a/google/cloud/dialogflow_v2/proto/conversation_event.proto b/google/cloud/dialogflow_v2/proto/conversation_event.proto new file mode 100644 index 000000000..61cfd75f6 --- /dev/null +++ b/google/cloud/dialogflow_v2/proto/conversation_event.proto @@ -0,0 +1,86 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2; + +import "google/cloud/dialogflow/v2/participant.proto"; +import "google/rpc/status.proto"; +import "google/api/annotations.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "ConversationEventProto"; +option java_package = "com.google.cloud.dialogflow.v2"; +option objc_class_prefix = "DF"; + +// Represents a notification sent to Pub/Sub subscribers for conversation +// lifecycle events. +message ConversationEvent { + // Enumeration of the types of events available. + enum Type { + // Type not set. + TYPE_UNSPECIFIED = 0; + + // A new conversation has been opened. This is fired when a telephone call + // is answered, or a conversation is created via the API. + CONVERSATION_STARTED = 1; + + // An existing conversation has closed. This is fired when a telephone call + // is terminated, or a conversation is closed via the API. + CONVERSATION_FINISHED = 2; + + // An existing conversation has received notification from Dialogflow that + // human intervention is required. + HUMAN_INTERVENTION_NEEDED = 3; + + // An existing conversation has received a new message, either from API or + // telephony. It is configured in + // [ConversationProfile.new_message_event_notification_config][google.cloud.dialogflow.v2.ConversationProfile.new_message_event_notification_config] + NEW_MESSAGE = 5; + + // Unrecoverable error during a telephone call. + // + // In general non-recoverable errors only occur if something was + // misconfigured in the ConversationProfile corresponding to the call. After + // a non-recoverable error, Dialogflow may stop responding. + // + // We don't fire this event: + // + // * in an API call because we can directly return the error, or, + // * when we can recover from an error. + UNRECOVERABLE_ERROR = 4; + } + + // The unique identifier of the conversation this notification + // refers to. + // Format: `projects//conversations/`. + string conversation = 1; + + // The type of the event that this notification refers to. + Type type = 2; + + // More detailed information about an error. Only set for type + // UNRECOVERABLE_ERROR_IN_PHONE_CALL. + google.rpc.Status error_status = 3; + + // Payload of conversation event. + oneof payload { + // Payload of NEW_MESSAGE event. + Message new_message_payload = 4; + } +} diff --git a/google/cloud/dialogflow_v2/proto/conversation_profile.proto b/google/cloud/dialogflow_v2/proto/conversation_profile.proto new file mode 100644 index 000000000..ea0fc6f02 --- /dev/null +++ b/google/cloud/dialogflow_v2/proto/conversation_profile.proto @@ -0,0 +1,577 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/cloud/dialogflow/v2/audio_config.proto"; +import "google/cloud/dialogflow/v2/participant.proto"; +import "google/longrunning/operations.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "ConversationProfileProto"; +option java_package = "com.google.cloud.dialogflow.v2"; +option objc_class_prefix = "DF"; + +// Service for managing [ConversationProfiles][google.cloud.dialogflow.v2.ConversationProfile]. +service ConversationProfiles { + option (google.api.default_host) = "dialogflow.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/dialogflow"; + + // Returns the list of all conversation profiles in the specified project. + rpc ListConversationProfiles(ListConversationProfilesRequest) returns (ListConversationProfilesResponse) { + option (google.api.http) = { + get: "/v2/{parent=projects/*}/conversationProfiles" + additional_bindings { + get: "/v2/{parent=projects/*/locations/*}/conversationProfiles" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Retrieves the specified conversation profile. + rpc GetConversationProfile(GetConversationProfileRequest) returns (ConversationProfile) { + option (google.api.http) = { + get: "/v2/{name=projects/*/conversationProfiles/*}" + additional_bindings { + get: "/v2/{name=projects/*/locations/*/conversationProfiles/*}" + } + }; + option (google.api.method_signature) = "name"; + } + + // Creates a conversation profile in the specified project. + // + // [ConversationProfile.CreateTime][] and [ConversationProfile.UpdateTime][] + // aren't populated in the response. You can retrieve them via + // [GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile] API. + rpc CreateConversationProfile(CreateConversationProfileRequest) returns (ConversationProfile) { + option (google.api.http) = { + post: "/v2/{parent=projects/*}/conversationProfiles" + body: "conversation_profile" + additional_bindings { + post: "/v2/{parent=projects/*/locations/*}/conversationProfiles" + body: "conversation_profile" + } + }; + option (google.api.method_signature) = "parent,conversation_profile"; + } + + // Updates the specified conversation profile. + // + // [ConversationProfile.CreateTime][] and [ConversationProfile.UpdateTime][] + // aren't populated in the response. You can retrieve them via + // [GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile] API. + rpc UpdateConversationProfile(UpdateConversationProfileRequest) returns (ConversationProfile) { + option (google.api.http) = { + patch: "/v2/{conversation_profile.name=projects/*/conversationProfiles/*}" + body: "conversation_profile" + additional_bindings { + patch: "/v2/{conversation_profile.name=projects/*/locations/*/conversationProfiles/*}" + body: "conversation_profile" + } + }; + option (google.api.method_signature) = "conversation_profile,update_mask"; + } + + // Deletes the specified conversation profile. + rpc DeleteConversationProfile(DeleteConversationProfileRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v2/{name=projects/*/conversationProfiles/*}" + additional_bindings { + delete: "/v2/{name=projects/*/locations/*/conversationProfiles/*}" + } + }; + option (google.api.method_signature) = "name"; + } +} + +// Defines the services to connect to incoming Dialogflow conversations. +message ConversationProfile { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/ConversationProfile" + pattern: "projects/{project}/conversationProfiles/{conversation_profile}" + pattern: "projects/{project}/locations/{location}/conversationProfiles/{conversation_profile}" + }; + + // Optional. The unique identifier of this conversation profile. + // Format: `projects//locations//conversationProfiles/`. + string name = 1 [(google.api.field_behavior) = OPTIONAL]; + + // Required. Human readable name for this profile. Max length 1024 bytes. + string display_name = 2 [(google.api.field_behavior) = REQUIRED]; + + // Output only. Create time of the conversation profile. + google.protobuf.Timestamp create_time = 11 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. Update time of the conversation profile. + google.protobuf.Timestamp update_time = 12 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Configuration for an automated agent to use with this profile. + AutomatedAgentConfig automated_agent_config = 3; + + // Configuration for agent assistance to use with this profile. + HumanAgentAssistantConfig human_agent_assistant_config = 4; + + // Configuration for connecting to a live agent. + HumanAgentHandoffConfig human_agent_handoff_config = 5; + + // Configuration for publishing conversation lifecycle events. + NotificationConfig notification_config = 6; + + // Configuration for logging conversation lifecycle events. + LoggingConfig logging_config = 7; + + // Configuration for publishing new message events. Event will be sent in + // format of [ConversationEvent][google.cloud.dialogflow.v2.ConversationEvent] + NotificationConfig new_message_event_notification_config = 8; + + // Settings for speech transcription. + SpeechToTextConfig stt_config = 9; + + // Language which represents the conversationProfile. + // If unspecified, the default language code en-us applies. Users need to + // create a ConversationProfile for each language they want to support. + string language_code = 10; +} + +// The request message for [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2.ConversationProfiles.ListConversationProfiles]. +message ListConversationProfilesRequest { + // Required. The project to list all conversation profiles from. + // Format: `projects//locations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/ConversationProfile" + } + ]; + + // The maximum number of items to return in a single page. By + // default 100 and at most 1000. + int32 page_size = 2; + + // The next_page_token value returned from a previous list request. + string page_token = 3; +} + +// The response message for [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2.ConversationProfiles.ListConversationProfiles]. +message ListConversationProfilesResponse { + // The list of project conversation profiles. There is a maximum number + // of items returned based on the page_size field in the request. + repeated ConversationProfile conversation_profiles = 1; + + // Token to retrieve the next page of results, or empty if there are no + // more results in the list. + string next_page_token = 2; +} + +// The request message for [ConversationProfiles.GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile]. +message GetConversationProfileRequest { + // Required. The resource name of the conversation profile. + // Format: `projects//locations//conversationProfiles/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/ConversationProfile" + } + ]; +} + +// The request message for [ConversationProfiles.CreateConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.CreateConversationProfile]. +message CreateConversationProfileRequest { + // Required. The project to create a conversation profile for. + // Format: `projects//locations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/ConversationProfile" + } + ]; + + // Required. The conversation profile to create. + ConversationProfile conversation_profile = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request message for [ConversationProfiles.UpdateConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.UpdateConversationProfile]. +message UpdateConversationProfileRequest { + // Required. The conversation profile to update. + ConversationProfile conversation_profile = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The mask to control which fields to update. + google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request message for [ConversationProfiles.DeleteConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.DeleteConversationProfile]. +// +// This operation fails if the conversation profile is still referenced from +// a phone number. +message DeleteConversationProfileRequest { + // Required. The name of the conversation profile to delete. + // Format: `projects//locations//conversationProfiles/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/ConversationProfile" + } + ]; +} + +// Defines the Automated Agent to connect to a conversation. +message AutomatedAgentConfig { + // Required. ID of the Dialogflow agent environment to use. + // + // This project needs to either be the same project as the conversation or you + // need to grant `service-@gcp-sa-dialogflow.iam.gserviceaccount.com` the `Dialogflow API + // Service Agent` role in this project. + // + // Format: `projects//locations//agent/environments/`. If environment is not + // specified, the default `draft` environment is used. Refer to + // [DetectIntentRequest](/dialogflow/docs/reference/rpc/google.cloud.dialogflow.v2#google.cloud.dialogflow.v2.DetectIntentRequest) + // for more details. + string agent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Agent" + } + ]; +} + +// Defines the Human Agent Assist to connect to a conversation. +message HumanAgentAssistantConfig { + // Settings of suggestion trigger. + message SuggestionTriggerSettings { + // Do not trigger if last utterance is small talk. + bool no_smalltalk = 1; + + // Only trigger suggestion if participant role of last utterance is + // END_USER. + bool only_end_user = 2; + } + + // Config for suggestion features. + message SuggestionFeatureConfig { + // The suggestion feature. + SuggestionFeature suggestion_feature = 5; + + // Automatically iterates all participants and tries to compile + // suggestions. + // + // Supported features: ARTICLE_SUGGESTION, FAQ, DIALOGFLOW_ASSIST. + bool enable_event_based_suggestion = 3; + + // Settings of suggestion trigger. + // + // Currently, only ARTICLE_SUGGESTION and FAQ will use this field. + SuggestionTriggerSettings suggestion_trigger_settings = 10; + + // Configs of query. + SuggestionQueryConfig query_config = 6; + + // Configs of custom conversation model. + ConversationModelConfig conversation_model_config = 7; + } + + // Detail human agent assistant config. + message SuggestionConfig { + // Configuration of different suggestion features. One feature can have only + // one config. + repeated SuggestionFeatureConfig feature_configs = 2; + + // If `group_suggestion_responses` is false, and there are multiple + // `feature_configs` in `event based suggestion` or + // StreamingAnalyzeContent, we will try to deliver suggestions to customers + // as soon as we get new suggestion. Different type of suggestions based on + // the same context will be in separate Pub/Sub event or + // `StreamingAnalyzeContentResponse`. + // + // If `group_suggestion_responses` set to true. All the suggestions to the + // same participant based on the same context will be grouped into a single + // Pub/Sub event or StreamingAnalyzeContentResponse. + bool group_suggestion_responses = 3; + } + + // Config for suggestion query. + message SuggestionQueryConfig { + // Knowledge base source settings. + // + // Supported features: ARTICLE_SUGGESTION, FAQ. + message KnowledgeBaseQuerySource { + // Required. Knowledge bases to query. Format: + // `projects//locations//knowledgeBases/`. Currently, at most 5 knowledge + // bases are supported. + repeated string knowledge_bases = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/KnowledgeBase" + } + ]; + } + + // Document source settings. + // + // Supported features: SMART_REPLY, SMART_COMPOSE. + message DocumentQuerySource { + // Required. Knowledge documents to query from. Format: + // `projects//locations//knowledgeBases//documents/`. + // Currently, at most 5 documents are supported. + repeated string documents = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Document" + } + ]; + } + + // Dialogflow source setting. + // + // Supported feature: DIALOGFLOW_ASSIST. + message DialogflowQuerySource { + // Required. The name of a Dialogflow virtual agent used for end user side intent + // detection and suggestion. Format: `projects//locations//agent`. When multiple agents are allowed in + // the same Dialogflow project. + string agent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Agent" + } + ]; + } + + // Settings that determine how to filter recent conversation context when + // generating suggestions. + message ContextFilterSettings { + // If set to true, the last message from virtual agent (hand off message) + // and the message before it (trigger message of hand off) are dropped. + bool drop_handoff_messages = 1; + + // If set to true, all messages from virtual agent are dropped. + bool drop_virtual_agent_messages = 2; + + // If set to true, all messages from ivr stage are dropped. + bool drop_ivr_messages = 3; + } + + // Source of query. + oneof query_source { + // Query from knowledgebase. It is used by: + // ARTICLE_SUGGESTION, FAQ. + KnowledgeBaseQuerySource knowledge_base_query_source = 1; + + // Query from knowledge base document. It is used by: + // SMART_REPLY, SMART_COMPOSE. + DocumentQuerySource document_query_source = 2; + + // Query from Dialogflow agent. It is used by DIALOGFLOW_ASSIST. + DialogflowQuerySource dialogflow_query_source = 3; + } + + // Maximum number of results to return. Currently, if unset, defaults to 10. + // And the max number is 20. + int32 max_results = 4; + + // Confidence threshold of query result. + // + // Agent Assist gives each suggestion a score in the range [0.0, 1.0], based + // on the relevance between the suggestion and the current conversation + // context. A score of 0.0 has no relevance, while a score of 1.0 has high + // relevance. Only suggestions with a score greater than or equal to the + // value of this field are included in the results. + // + // For a baseline model (the default), the recommended value is in the range + // [0.05, 0.1]. + // + // For a custom model, there is no recommended value. Tune this value by + // starting from a very low value and slowly increasing until you have + // desired results. + // + // If this field is not set, it defaults to 0.0, which means that all + // suggestions are returned. + // + // Supported features: ARTICLE_SUGGESTION. + float confidence_threshold = 5; + + // Determines how recent conversation context is filtered when generating + // suggestions. If unspecified, no messages will be dropped. + ContextFilterSettings context_filter_settings = 7; + } + + // Custom conversation models used in agent assist feature. + // + // Supported feature: ARTICLE_SUGGESTION, SMART_COMPOSE, SMART_REPLY. + message ConversationModelConfig { + // Required. Conversation model resource name. Format: `projects//conversationModels/`. + string model = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/ConversationModel" + } + ]; + } + + // Configuration for analyses to run on each conversation message. + message MessageAnalysisConfig { + // Enable entity extraction in conversation messages on [agent assist + // stage](https://cloud.google.com/dialogflow/priv/docs/contact-center/basics#stages). + // If unspecified, defaults to false. + bool enable_entity_extraction = 2; + + // Enable sentiment analysis in conversation messages on [agent assist + // stage](https://cloud.google.com/dialogflow/priv/docs/contact-center/basics#stages). + // If unspecified, defaults to false. Sentiment analysis inspects user input + // and identifies the prevailing subjective opinion, especially to determine + // a user's attitude as positive, negative, or neutral: + // https://cloud.google.com/natural-language/docs/basics#sentiment_analysis + // For [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2.Participants.StreamingAnalyzeContent] method, result will be in + // [StreamingAnalyzeContentResponse.message.SentimentAnalysisResult][google.cloud.dialogflow.v2.StreamingAnalyzeContentResponse.message]. + // For [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent] method, result will be in + // [AnalyzeContentResponse.message.SentimentAnalysisResult][google.cloud.dialogflow.v2.AnalyzeContentResponse.message] + // For [Conversations.ListMessages][google.cloud.dialogflow.v2.Conversations.ListMessages] method, result will be in + // [ListMessagesResponse.messages.SentimentAnalysisResult][google.cloud.dialogflow.v2.ListMessagesResponse.messages] + // If Pub/Sub notification is configured, result will be in + // [ConversationEvent.new_message_payload.SentimentAnalysisResult][google.cloud.dialogflow.v2.ConversationEvent.new_message_payload]. + bool enable_sentiment_analysis = 3; + } + + // Pub/Sub topic on which to publish new agent assistant events. + NotificationConfig notification_config = 2; + + // Configuration for agent assistance of human agent participant. + SuggestionConfig human_agent_suggestion_config = 3; + + // Configuration for agent assistance of end user participant. + SuggestionConfig end_user_suggestion_config = 4; + + // Configuration for message analysis. + MessageAnalysisConfig message_analysis_config = 5; +} + +// Defines the hand off to a live agent, typically on which external agent +// service provider to connect to a conversation. +message HumanAgentHandoffConfig { + // Configuration specific to LivePerson (https://www.liveperson.com). + message LivePersonConfig { + // Required. Account number of the LivePerson account to connect. This is + // the account number you input at the login page. + string account_number = 1 [(google.api.field_behavior) = REQUIRED]; + } + + // Configuration specific to Salesforce Live Agent. + message SalesforceLiveAgentConfig { + // Required. The organization ID of the Salesforce account. + string organization_id = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. Live Agent deployment ID. + string deployment_id = 2 [(google.api.field_behavior) = REQUIRED]; + + // Required. Live Agent chat button ID. + string button_id = 3 [(google.api.field_behavior) = REQUIRED]; + + // Required. Domain of the Live Agent endpoint for this agent. You can find + // the endpoint URL in the `Live Agent settings` page. For example if URL + // has the form https://d.la4-c2-phx.salesforceliveagent.com/..., + // you should fill in d.la4-c2-phx.salesforceliveagent.com. + string endpoint_domain = 4 [(google.api.field_behavior) = REQUIRED]; + } + + // Required. Specifies which agent service to connect for human agent handoff. + oneof agent_service { + // Uses LivePerson (https://www.liveperson.com). + LivePersonConfig live_person_config = 1; + + // Uses Salesforce Live Agent. + SalesforceLiveAgentConfig salesforce_live_agent_config = 2; + } +} + +// Defines notification behavior. +message NotificationConfig { + // Format of cloud pub/sub message. + enum MessageFormat { + // If it is unspeified, PROTO will be used. + MESSAGE_FORMAT_UNSPECIFIED = 0; + + // Pubsub message will be serialized proto. + PROTO = 1; + + // Pubsub message will be json. + JSON = 2; + } + + // Name of the Pub/Sub topic to publish conversation + // events like + // [CONVERSATION_STARTED][google.cloud.dialogflow.v2.ConversationEvent.Type.CONVERSATION_STARTED] as + // serialized [ConversationEvent][google.cloud.dialogflow.v2.ConversationEvent] protos. + // + // Notification works for phone calls, if this topic either is in the same + // project as the conversation or you grant `service-@gcp-sa-dialogflow.iam.gserviceaccount.com` the `Dialogflow Service + // Agent` role in the topic project. + // + // Format: `projects//locations//topics/`. + string topic = 1; + + // Format of message. + MessageFormat message_format = 2; +} + +// Defines logging behavior for conversation lifecycle events. +message LoggingConfig { + // Whether to log conversation events like + // [CONVERSATION_STARTED][google.cloud.dialogflow.v2.ConversationEvent.Type.CONVERSATION_STARTED] to + // Stackdriver in the conversation project as JSON format + // [ConversationEvent][google.cloud.dialogflow.v2.ConversationEvent] protos. + bool enable_stackdriver_logging = 3; +} + +// The type of Human Agent Assistant API suggestion to perform, and the maximum +// number of results to return for that type. Multiple `Feature` objects can +// be specified in the `features` list. +message SuggestionFeature { + // Defines the type of Human Agent Assistant feature. + enum Type { + // Unspecified feature type. + TYPE_UNSPECIFIED = 0; + + // Run article suggestion model. + ARTICLE_SUGGESTION = 1; + + // Run FAQ model. + FAQ = 2; + } + + // Type of Human Agent Assistant API feature to request. + Type type = 1; +} diff --git a/google/cloud/dialogflow_v2/proto/document.proto b/google/cloud/dialogflow_v2/proto/document.proto new file mode 100644 index 000000000..5ff579960 --- /dev/null +++ b/google/cloud/dialogflow_v2/proto/document.proto @@ -0,0 +1,389 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/longrunning/operations.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; +import "google/rpc/status.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "DocumentProto"; +option java_package = "com.google.cloud.dialogflow.v2"; +option objc_class_prefix = "DF"; + +// Service for managing knowledge [Documents][google.cloud.dialogflow.v2.Document]. +service Documents { + option (google.api.default_host) = "dialogflow.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/dialogflow"; + + // Returns the list of all documents of the knowledge base. + rpc ListDocuments(ListDocumentsRequest) returns (ListDocumentsResponse) { + option (google.api.http) = { + get: "/v2/{parent=projects/*/knowledgeBases/*}/documents" + additional_bindings { + get: "/v2/{parent=projects/*/locations/*/knowledgeBases/*}/documents" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Retrieves the specified document. + rpc GetDocument(GetDocumentRequest) returns (Document) { + option (google.api.http) = { + get: "/v2/{name=projects/*/knowledgeBases/*/documents/*}" + additional_bindings { + get: "/v2/{name=projects/*/locations/*/knowledgeBases/*/documents/*}" + } + }; + option (google.api.method_signature) = "name"; + } + + // Creates a new document. + // + // Operation + rpc CreateDocument(CreateDocumentRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v2/{parent=projects/*/knowledgeBases/*}/documents" + body: "document" + additional_bindings { + post: "/v2/{parent=projects/*/locations/*/knowledgeBases/*}/documents" + body: "document" + } + }; + option (google.api.method_signature) = "parent,document"; + option (google.longrunning.operation_info) = { + response_type: "Document" + metadata_type: "KnowledgeOperationMetadata" + }; + } + + // Deletes the specified document. + // + // Operation + rpc DeleteDocument(DeleteDocumentRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + delete: "/v2/{name=projects/*/knowledgeBases/*/documents/*}" + additional_bindings { + delete: "/v2/{name=projects/*/locations/*/knowledgeBases/*/documents/*}" + } + }; + option (google.api.method_signature) = "name"; + option (google.longrunning.operation_info) = { + response_type: "google.protobuf.Empty" + metadata_type: "KnowledgeOperationMetadata" + }; + } + + // Updates the specified document. + // + // Operation + rpc UpdateDocument(UpdateDocumentRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + patch: "/v2/{document.name=projects/*/knowledgeBases/*/documents/*}" + body: "document" + additional_bindings { + patch: "/v2/{document.name=projects/*/locations/*/knowledgeBases/*/documents/*}" + body: "document" + } + }; + option (google.api.method_signature) = "document,update_mask"; + option (google.longrunning.operation_info) = { + response_type: "Document" + metadata_type: "KnowledgeOperationMetadata" + }; + } + + // Reloads the specified document from its specified source, content_uri or + // content. The previously loaded content of the document will be deleted. + // Note: Even when the content of the document has not changed, there still + // may be side effects because of internal implementation changes. + // + // Note: The `projects.agent.knowledgeBases.documents` resource is deprecated; + // only use `projects.knowledgeBases.documents`. + // + // Operation + rpc ReloadDocument(ReloadDocumentRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v2/{name=projects/*/knowledgeBases/*/documents/*}:reload" + body: "*" + additional_bindings { + post: "/v2/{name=projects/*/locations/*/knowledgeBases/*/documents/*}:reload" + body: "*" + } + }; + option (google.api.method_signature) = "name,content_uri"; + option (google.longrunning.operation_info) = { + response_type: "Document" + metadata_type: "KnowledgeOperationMetadata" + }; + } +} + +// A knowledge document to be used by a [KnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBase]. +// +// For more information, see the [knowledge base +// guide](https://cloud.google.com/dialogflow/docs/how/knowledge-bases). +// +// Note: The `projects.agent.knowledgeBases.documents` resource is deprecated; +// only use `projects.knowledgeBases.documents`. +message Document { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/Document" + pattern: "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}" + pattern: "projects/{project}/locations/{location}/knowledgeBases/{knowledge_base}/documents/{document}" + }; + + // The status of a reload attempt. + message ReloadStatus { + // The time of a reload attempt. + // This reload may have been triggered automatically or manually and may + // not have succeeded. + google.protobuf.Timestamp time = 1; + + // The status of a reload attempt or the initial load. + google.rpc.Status status = 2; + } + + // The knowledge type of document content. + enum KnowledgeType { + // The type is unspecified or arbitrary. + KNOWLEDGE_TYPE_UNSPECIFIED = 0; + + // The document content contains question and answer pairs as either HTML or + // CSV. Typical FAQ HTML formats are parsed accurately, but unusual formats + // may fail to be parsed. + // + // CSV must have questions in the first column and answers in the second, + // with no header. Because of this explicit format, they are always parsed + // accurately. + FAQ = 1; + + // Documents for which unstructured text is extracted and used for + // question answering. + EXTRACTIVE_QA = 2; + + // The entire document content as a whole can be used for query results. + // Only for Contact Center Solutions on Dialogflow. + ARTICLE_SUGGESTION = 3; + } + + // Optional. The document resource name. + // The name must be empty when creating a document. + // Format: `projects//locations//knowledgeBases//documents/`. + string name = 1 [(google.api.field_behavior) = OPTIONAL]; + + // Required. The display name of the document. The name must be 1024 bytes or + // less; otherwise, the creation request fails. + string display_name = 2 [(google.api.field_behavior) = REQUIRED]; + + // Required. The MIME type of this document. + string mime_type = 3 [(google.api.field_behavior) = REQUIRED]; + + // Required. The knowledge type of document content. + repeated KnowledgeType knowledge_types = 4 [(google.api.field_behavior) = REQUIRED]; + + // Required. The source of this document. + oneof source { + // The URI where the file content is located. + // + // For documents stored in Google Cloud Storage, these URIs must have + // the form `gs:///`. + // + // NOTE: External URLs must correspond to public webpages, i.e., they must + // be indexed by Google Search. In particular, URLs for showing documents in + // Google Cloud Storage (i.e. the URL in your browser) are not supported. + // Instead use the `gs://` format URI described above. + string content_uri = 5; + + // The raw content of the document. This field is only permitted for + // EXTRACTIVE_QA and FAQ knowledge types. + bytes raw_content = 9; + } + + // Optional. If true, we try to automatically reload the document every day + // (at a time picked by the system). If false or unspecified, we don't try + // to automatically reload the document. + // + // Currently you can only enable automatic reload for documents sourced from + // a public url, see `source` field for the source types. + // + // Reload status can be tracked in `latest_reload_status`. If a reload + // fails, we will keep the document unchanged. + // + // If a reload fails with internal errors, the system will try to reload the + // document on the next day. + // If a reload fails with non-retriable errors (e.g. PERMISION_DENIED), the + // system will not try to reload the document anymore. You need to manually + // reload the document successfully by calling `ReloadDocument` and clear the + // errors. + bool enable_auto_reload = 11 [(google.api.field_behavior) = OPTIONAL]; + + // Output only. The time and status of the latest reload. + // This reload may have been triggered automatically or manually + // and may not have succeeded. + ReloadStatus latest_reload_status = 12 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Optional. Metadata for the document. The metadata supports arbitrary + // key-value pairs. Suggested use cases include storing a document's title, + // an external URL distinct from the document's content_uri, etc. + // The max size of a `key` or a `value` of the metadata is 1024 bytes. + map metadata = 7 [(google.api.field_behavior) = OPTIONAL]; +} + +// Request message for [Documents.GetDocument][google.cloud.dialogflow.v2.Documents.GetDocument]. +message GetDocumentRequest { + // Required. The name of the document to retrieve. + // Format `projects//locations//knowledgeBases//documents/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Document" + } + ]; +} + +// Request message for [Documents.ListDocuments][google.cloud.dialogflow.v2.Documents.ListDocuments]. +message ListDocumentsRequest { + // Required. The knowledge base to list all documents for. + // Format: `projects//locations//knowledgeBases/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Document" + } + ]; + + // The maximum number of items to return in a single page. By + // default 10 and at most 100. + int32 page_size = 2; + + // The next_page_token value returned from a previous list request. + string page_token = 3; +} + +// Response message for [Documents.ListDocuments][google.cloud.dialogflow.v2.Documents.ListDocuments]. +message ListDocumentsResponse { + // The list of documents. + repeated Document documents = 1; + + // Token to retrieve the next page of results, or empty if there are no + // more results in the list. + string next_page_token = 2; +} + +// Request message for [Documents.CreateDocument][google.cloud.dialogflow.v2.Documents.CreateDocument]. +message CreateDocumentRequest { + // Required. The knowledge base to create a document for. + // Format: `projects//locations//knowledgeBases/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Document" + } + ]; + + // Required. The document to create. + Document document = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// Request message for [Documents.DeleteDocument][google.cloud.dialogflow.v2.Documents.DeleteDocument]. +message DeleteDocumentRequest { + // Required. The name of the document to delete. + // Format: `projects//locations//knowledgeBases//documents/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Document" + } + ]; +} + +// Request message for [Documents.UpdateDocument][google.cloud.dialogflow.v2.Documents.UpdateDocument]. +message UpdateDocumentRequest { + // Required. The document to update. + Document document = 1 [(google.api.field_behavior) = REQUIRED]; + + // Optional. Not specified means `update all`. + // Currently, only `display_name` can be updated, an InvalidArgument will be + // returned for attempting to update other fields. + google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = OPTIONAL]; +} + +// Request message for [Documents.ReloadDocument][google.cloud.dialogflow.v2.Documents.ReloadDocument]. +message ReloadDocumentRequest { + // Required. The name of the document to reload. + // Format: `projects//locations//knowledgeBases//documents/` + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Document" + } + ]; + + // The source for document reloading. + // If provided, the service will load the contents from the source + // and update document in the knowledge base. + oneof source { + // Optional. The path of gcs source file for reloading document content. For now, + // only gcs uri is supported. + // + // For documents stored in Google Cloud Storage, these URIs must have + // the form `gs:///`. + string content_uri = 3 [(google.api.field_behavior) = OPTIONAL]; + } +} + +// Metadata in google::longrunning::Operation for Knowledge operations. +message KnowledgeOperationMetadata { + // States of the operation. + enum State { + // State unspecified. + STATE_UNSPECIFIED = 0; + + // The operation has been created. + PENDING = 1; + + // The operation is currently running. + RUNNING = 2; + + // The operation is done, either cancelled or completed. + DONE = 3; + } + + // Output only. The current state of this operation. + State state = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; +} diff --git a/google/cloud/dialogflow_v2/proto/entity_type.proto b/google/cloud/dialogflow_v2/proto/entity_type.proto index 13fcdb050..f8a69ec58 100644 --- a/google/cloud/dialogflow_v2/proto/entity_type.proto +++ b/google/cloud/dialogflow_v2/proto/entity_type.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/google/cloud/dialogflow_v2/proto/environment.proto b/google/cloud/dialogflow_v2/proto/environment.proto index d48c210fc..ef094c71e 100644 --- a/google/cloud/dialogflow_v2/proto/environment.proto +++ b/google/cloud/dialogflow_v2/proto/environment.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/google/cloud/dialogflow_v2/proto/gcs.proto b/google/cloud/dialogflow_v2/proto/gcs.proto new file mode 100644 index 000000000..1fb2dc99c --- /dev/null +++ b/google/cloud/dialogflow_v2/proto/gcs.proto @@ -0,0 +1,28 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2; + +import "google/api/field_behavior.proto"; +import "google/api/annotations.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "GcsProto"; +option java_package = "com.google.cloud.dialogflow.v2"; +option objc_class_prefix = "DF"; diff --git a/google/cloud/dialogflow_v2/proto/human_agent_assistant_event.proto b/google/cloud/dialogflow_v2/proto/human_agent_assistant_event.proto new file mode 100644 index 000000000..f79ce9d46 --- /dev/null +++ b/google/cloud/dialogflow_v2/proto/human_agent_assistant_event.proto @@ -0,0 +1,44 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2; + +import "google/cloud/dialogflow/v2/participant.proto"; +import "google/api/annotations.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "HumanAgentAssistantEventProto"; +option java_package = "com.google.cloud.dialogflow.v2"; +option objc_class_prefix = "DF"; + +// Represents a notification sent to Cloud Pub/Sub subscribers for +// human agent assistant events in a specific conversation. +message HumanAgentAssistantEvent { + // The conversation this notification refers to. + // Format: `projects//conversations/`. + string conversation = 1; + + // The participant that the suggestion is compiled for. + // Format: `projects//conversations//participants/`. It will not be set in legacy workflow. + string participant = 3; + + // The suggestion results payload that this notification refers to. + repeated SuggestionResult suggestion_results = 5; +} diff --git a/google/cloud/dialogflow_v2/proto/intent.proto b/google/cloud/dialogflow_v2/proto/intent.proto index f4f78acad..e1507a5c9 100644 --- a/google/cloud/dialogflow_v2/proto/intent.proto +++ b/google/cloud/dialogflow_v2/proto/intent.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -47,6 +47,9 @@ service Intents { rpc ListIntents(ListIntentsRequest) returns (ListIntentsResponse) { option (google.api.http) = { get: "/v2/{parent=projects/*/agent}/intents" + additional_bindings { + get: "/v2/{parent=projects/*/agent/environments/*}/intents" + } }; option (google.api.method_signature) = "parent"; option (google.api.method_signature) = "parent,language_code"; @@ -771,6 +774,17 @@ message Intent { // auto-markup in the UI is turned off. bool ml_disabled = 19 [(google.api.field_behavior) = OPTIONAL]; + // Optional. Indicates that a live agent should be brought in to handle the + // interaction with the user. In most cases, when you set this flag to true, + // you would also want to set end_interaction to true as well. Default is + // false. + bool live_agent_handoff = 20 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. Indicates that this intent ends an interaction. Some integrations + // (e.g., Actions on Google or Dialogflow phone gateway) use this information + // to close interaction with an end user. Default is false. + bool end_interaction = 21 [(google.api.field_behavior) = OPTIONAL]; + // Optional. The list of context names required for this intent to be // triggered. // Format: `projects//agent/sessions/-/contexts/`. diff --git a/google/cloud/dialogflow_v2/proto/knowledge_base.proto b/google/cloud/dialogflow_v2/proto/knowledge_base.proto new file mode 100644 index 000000000..52dbed10c --- /dev/null +++ b/google/cloud/dialogflow_v2/proto/knowledge_base.proto @@ -0,0 +1,217 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "KnowledgeBaseProto"; +option java_package = "com.google.cloud.dialogflow.v2"; +option objc_class_prefix = "DF"; + +// Service for managing [KnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBase]. +service KnowledgeBases { + option (google.api.default_host) = "dialogflow.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/dialogflow"; + + // Returns the list of all knowledge bases of the specified agent. + rpc ListKnowledgeBases(ListKnowledgeBasesRequest) returns (ListKnowledgeBasesResponse) { + option (google.api.http) = { + get: "/v2/{parent=projects/*}/knowledgeBases" + additional_bindings { + get: "/v2/{parent=projects/*/locations/*}/knowledgeBases" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Retrieves the specified knowledge base. + rpc GetKnowledgeBase(GetKnowledgeBaseRequest) returns (KnowledgeBase) { + option (google.api.http) = { + get: "/v2/{name=projects/*/knowledgeBases/*}" + additional_bindings { + get: "/v2/{name=projects/*/locations/*/knowledgeBases/*}" + } + }; + option (google.api.method_signature) = "name"; + } + + // Creates a knowledge base. + rpc CreateKnowledgeBase(CreateKnowledgeBaseRequest) returns (KnowledgeBase) { + option (google.api.http) = { + post: "/v2/{parent=projects/*}/knowledgeBases" + body: "knowledge_base" + additional_bindings { + post: "/v2/{parent=projects/*/locations/*}/knowledgeBases" + body: "knowledge_base" + } + }; + option (google.api.method_signature) = "parent,knowledge_base"; + } + + // Deletes the specified knowledge base. + rpc DeleteKnowledgeBase(DeleteKnowledgeBaseRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v2/{name=projects/*/knowledgeBases/*}" + additional_bindings { + delete: "/v2/{name=projects/*/locations/*/knowledgeBases/*}" + } + }; + option (google.api.method_signature) = "name"; + } + + // Updates the specified knowledge base. + rpc UpdateKnowledgeBase(UpdateKnowledgeBaseRequest) returns (KnowledgeBase) { + option (google.api.http) = { + patch: "/v2/{knowledge_base.name=projects/*/knowledgeBases/*}" + body: "knowledge_base" + additional_bindings { + patch: "/v2/{knowledge_base.name=projects/*/locations/*/knowledgeBases/*}" + body: "knowledge_base" + } + }; + option (google.api.method_signature) = "knowledge_base,update_mask"; + } +} + +// A knowledge base represents a collection of knowledge documents that you +// provide to Dialogflow. Your knowledge documents contain information that may +// be useful during conversations with end-users. Some Dialogflow features use +// knowledge bases when looking for a response to an end-user input. +// +// For more information, see the [knowledge base +// guide](https://cloud.google.com/dialogflow/docs/how/knowledge-bases). +// +// Note: The `projects.agent.knowledgeBases` resource is deprecated; +// only use `projects.knowledgeBases`. +message KnowledgeBase { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/KnowledgeBase" + pattern: "projects/{project}/knowledgeBases/{knowledge_base}" + pattern: "projects/{project}/locations/{location}/knowledgeBases/{knowledge_base}" + }; + + // The knowledge base resource name. + // The name must be empty when creating a knowledge base. + // Format: `projects//locations//knowledgeBases/`. + string name = 1; + + // Required. The display name of the knowledge base. The name must be 1024 + // bytes or less; otherwise, the creation request fails. + string display_name = 2 [(google.api.field_behavior) = REQUIRED]; + + // Language which represents the KnowledgeBase. When the KnowledgeBase is + // created/updated, expect this to be present for non en-us languages. When + // unspecified, the default language code en-us applies. + string language_code = 4; +} + +// Request message for [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBases.ListKnowledgeBases]. +message ListKnowledgeBasesRequest { + // Required. The project to list of knowledge bases for. + // Format: `projects//locations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/KnowledgeBase" + } + ]; + + // The maximum number of items to return in a single page. By + // default 10 and at most 100. + int32 page_size = 2; + + // The next_page_token value returned from a previous list request. + string page_token = 3; +} + +// Response message for [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBases.ListKnowledgeBases]. +message ListKnowledgeBasesResponse { + // The list of knowledge bases. + repeated KnowledgeBase knowledge_bases = 1; + + // Token to retrieve the next page of results, or empty if there are no + // more results in the list. + string next_page_token = 2; +} + +// Request message for [KnowledgeBases.GetKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.GetKnowledgeBase]. +message GetKnowledgeBaseRequest { + // Required. The name of the knowledge base to retrieve. + // Format `projects//locations//knowledgeBases/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/KnowledgeBase" + } + ]; +} + +// Request message for [KnowledgeBases.CreateKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.CreateKnowledgeBase]. +message CreateKnowledgeBaseRequest { + // Required. The project to create a knowledge base for. + // Format: `projects//locations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/KnowledgeBase" + } + ]; + + // Required. The knowledge base to create. + KnowledgeBase knowledge_base = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// Request message for [KnowledgeBases.DeleteKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.DeleteKnowledgeBase]. +message DeleteKnowledgeBaseRequest { + // Required. The name of the knowledge base to delete. + // Format: `projects//locations//knowledgeBases/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/KnowledgeBase" + } + ]; + + // Optional. Force deletes the knowledge base. When set to true, any documents + // in the knowledge base are also deleted. + bool force = 2 [(google.api.field_behavior) = OPTIONAL]; +} + +// Request message for [KnowledgeBases.UpdateKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.UpdateKnowledgeBase]. +message UpdateKnowledgeBaseRequest { + // Required. The knowledge base to update. + KnowledgeBase knowledge_base = 1 [(google.api.field_behavior) = REQUIRED]; + + // Optional. Not specified means `update all`. + // Currently, only `display_name` can be updated, an InvalidArgument will be + // returned for attempting to update other fields. + google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = OPTIONAL]; +} diff --git a/google/cloud/dialogflow_v2/proto/participant.proto b/google/cloud/dialogflow_v2/proto/participant.proto new file mode 100644 index 000000000..2c6e85736 --- /dev/null +++ b/google/cloud/dialogflow_v2/proto/participant.proto @@ -0,0 +1,766 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/cloud/dialogflow/v2/audio_config.proto"; +import "google/cloud/dialogflow/v2/session.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "google/rpc/status.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "ParticipantProto"; +option java_package = "com.google.cloud.dialogflow.v2"; +option objc_class_prefix = "DF"; + +// Service for managing [Participants][google.cloud.dialogflow.v2.Participant]. +service Participants { + option (google.api.default_host) = "dialogflow.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/dialogflow"; + + // Creates a new participant in a conversation. + rpc CreateParticipant(CreateParticipantRequest) returns (Participant) { + option (google.api.http) = { + post: "/v2/{parent=projects/*/conversations/*}/participants" + body: "participant" + additional_bindings { + post: "/v2/{parent=projects/*/locations/*/conversations/*}/participants" + body: "participant" + } + }; + option (google.api.method_signature) = "parent,participant"; + } + + // Retrieves a conversation participant. + rpc GetParticipant(GetParticipantRequest) returns (Participant) { + option (google.api.http) = { + get: "/v2/{name=projects/*/conversations/*/participants/*}" + additional_bindings { + get: "/v2/{name=projects/*/locations/*/conversations/*/participants/*}" + } + }; + option (google.api.method_signature) = "name"; + } + + // Returns the list of all participants in the specified conversation. + rpc ListParticipants(ListParticipantsRequest) returns (ListParticipantsResponse) { + option (google.api.http) = { + get: "/v2/{parent=projects/*/conversations/*}/participants" + additional_bindings { + get: "/v2/{parent=projects/*/locations/*/conversations/*}/participants" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Updates the specified participant. + rpc UpdateParticipant(UpdateParticipantRequest) returns (Participant) { + option (google.api.http) = { + patch: "/v2/{participant.name=projects/*/conversations/*/participants/*}" + body: "participant" + additional_bindings { + patch: "/v2/{participant.name=projects/*/locations/*/conversations/*/participants/*}" + body: "participant" + } + }; + option (google.api.method_signature) = "participant,update_mask"; + } + + // Adds a text (chat, for example), or audio (phone recording, for example) + // message from a participant into the conversation. + // + // Note: Always use agent versions for production traffic + // sent to virtual agents. See [Versions and + // environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + rpc AnalyzeContent(AnalyzeContentRequest) returns (AnalyzeContentResponse) { + option (google.api.http) = { + post: "/v2/{participant=projects/*/conversations/*/participants/*}:analyzeContent" + body: "*" + additional_bindings { + post: "/v2/{participant=projects/*/locations/*/conversations/*/participants/*}:analyzeContent" + body: "*" + } + }; + option (google.api.method_signature) = "participant,text_input"; + option (google.api.method_signature) = "participant,audio_input"; + option (google.api.method_signature) = "participant,event_input"; + } + + // Adds a text (chat, for example), or audio (phone recording, for example) + // message from a participant into the conversation. + // Note: This method is only available through the gRPC API (not REST). + // + // The top-level message sent to the client by the server is + // `StreamingAnalyzeContentResponse`. Multiple response messages can be + // returned in order. The first one or more messages contain the + // `recognition_result` field. Each result represents a more complete + // transcript of what the user said. The next message contains the + // `reply_text` field and potentially the `reply_audio` field. The message can + // also contain the `automated_agent_reply` field. + // + // Note: Always use agent versions for production traffic + // sent to virtual agents. See [Versions and + // environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + rpc StreamingAnalyzeContent(stream StreamingAnalyzeContentRequest) returns (stream StreamingAnalyzeContentResponse) { + } + + // Gets suggested articles for a participant based on specific historical + // messages. + rpc SuggestArticles(SuggestArticlesRequest) returns (SuggestArticlesResponse) { + option (google.api.http) = { + post: "/v2/{parent=projects/*/conversations/*/participants/*}/suggestions:suggestArticles" + body: "*" + additional_bindings { + post: "/v2/{parent=projects/*/locations/*/conversations/*/participants/*}/suggestions:suggestArticles" + body: "*" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Gets suggested faq answers for a participant based on specific historical + // messages. + rpc SuggestFaqAnswers(SuggestFaqAnswersRequest) returns (SuggestFaqAnswersResponse) { + option (google.api.http) = { + post: "/v2/{parent=projects/*/conversations/*/participants/*}/suggestions:suggestFaqAnswers" + body: "*" + additional_bindings { + post: "/v2/{parent=projects/*/locations/*/conversations/*/participants/*}/suggestions:suggestFaqAnswers" + body: "*" + } + }; + option (google.api.method_signature) = "parent"; + } +} + +// Represents a conversation participant (human agent, virtual agent, end-user). +message Participant { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/Participant" + pattern: "projects/{project}/conversations/{conversation}/participants/{participant}" + pattern: "projects/{project}/locations/{location}/conversations/{conversation}/participants/{participant}" + }; + + // Enumeration of the roles a participant can play in a conversation. + enum Role { + // Participant role not set. + ROLE_UNSPECIFIED = 0; + + // Participant is a human agent. + HUMAN_AGENT = 1; + + // Participant is an automated agent, such as a Dialogflow agent. + AUTOMATED_AGENT = 2; + + // Participant is an end user that has called or chatted with + // Dialogflow services. + END_USER = 3; + } + + // Optional. The unique identifier of this participant. + // Format: `projects//locations//conversations//participants/`. + string name = 1 [(google.api.field_behavior) = OPTIONAL]; + + // Immutable. The role this participant plays in the conversation. This field must be set + // during participant creation and is then immutable. + Role role = 2 [(google.api.field_behavior) = IMMUTABLE]; + + // Optional. Label applied to streams representing this participant in SIPREC + // XML metadata and SDP. This is used to assign transcriptions from that + // media stream to this participant. This field can be updated. + string sip_recording_media_label = 6 [(google.api.field_behavior) = OPTIONAL]; +} + +// Represents a message posted into a conversation. +message Message { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/Message" + pattern: "projects/{project}/conversations/{conversation}/messages/{message}" + pattern: "projects/{project}/locations/{location}/conversations/{conversation}/messages/{message}" + }; + + // The unique identifier of the message. + // Format: `projects//locations//conversations//messages/`. + string name = 1; + + // Required. The message content. + string content = 2 [(google.api.field_behavior) = REQUIRED]; + + // Optional. The message language. + // This should be a [BCP-47](https://www.rfc-editor.org/rfc/bcp/bcp47.txt) + // language tag. Example: "en-US". + string language_code = 3 [(google.api.field_behavior) = OPTIONAL]; + + // Output only. The participant that sends this message. + string participant = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The role of the participant. + Participant.Role participant_role = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The time when the message was created. + google.protobuf.Timestamp create_time = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The annotation for the message. + MessageAnnotation message_annotation = 7 [(google.api.field_behavior) = OUTPUT_ONLY]; +} + +// The request message for [Participants.CreateParticipant][google.cloud.dialogflow.v2.Participants.CreateParticipant]. +message CreateParticipantRequest { + // Required. Resource identifier of the conversation adding the participant. + // Format: `projects//locations//conversations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Participant" + } + ]; + + // Required. The participant to create. + Participant participant = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request message for [Participants.GetParticipant][google.cloud.dialogflow.v2.Participants.GetParticipant]. +message GetParticipantRequest { + // Required. The name of the participant. Format: + // `projects//locations//conversations//participants/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Participant" + } + ]; +} + +// The request message for [Participants.ListParticipants][google.cloud.dialogflow.v2.Participants.ListParticipants]. +message ListParticipantsRequest { + // Required. The conversation to list all participants from. + // Format: `projects//locations//conversations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Participant" + } + ]; + + // Optional. The maximum number of items to return in a single page. By + // default 100 and at most 1000. + int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The next_page_token value returned from a previous list request. + string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// The response message for [Participants.ListParticipants][google.cloud.dialogflow.v2.Participants.ListParticipants]. +message ListParticipantsResponse { + // The list of participants. There is a maximum number of items + // returned based on the page_size field in the request. + repeated Participant participants = 1; + + // Token to retrieve the next page of results or empty if there are no + // more results in the list. + string next_page_token = 2; +} + +// The request message for [Participants.UpdateParticipant][google.cloud.dialogflow.v2.Participants.UpdateParticipant]. +message UpdateParticipantRequest { + // Required. The participant to update. + Participant participant = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The mask to specify which fields to update. + google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request message for [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent]. +message AnalyzeContentRequest { + // Required. The name of the participant this text comes from. + // Format: `projects//locations//conversations//participants/`. + string participant = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Participant" + } + ]; + + // Required. The input content. + oneof input { + // The natural language text to be processed. + TextInput text_input = 6; + + // The natural language speech audio to be processed. + AudioInput audio_input = 7; + + // An input event to send to Dialogflow. + EventInput event_input = 8; + } + + // Speech synthesis configuration. + // The speech synthesis settings for a virtual agent that may be configured + // for the associated conversation profile are not used when calling + // AnalyzeContent. If this configuration is not supplied, speech synthesis + // is disabled. + OutputAudioConfig reply_audio_config = 5; + + // Parameters for a Dialogflow virtual-agent query. + QueryParameters query_params = 9; + + // A unique identifier for this request. Restricted to 36 ASCII characters. + // A random UUID is recommended. + // This request is only idempotent if a `request_id` is provided. + string request_id = 11; +} + +// The message in the response that indicates the parameters of DTMF. +message DtmfParameters { + // Indicates whether DTMF input can be handled in the next request. + bool accepts_dtmf_input = 1; +} + +// The response message for [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent]. +message AnalyzeContentResponse { + // The output text content. + // This field is set if the automated agent responded with text to show to + // the user. + string reply_text = 1; + + // The audio data bytes encoded as specified in the request. + // This field is set if: + // + // - `reply_audio_config` was specified in the request, or + // - The automated agent responded with audio to play to the user. In such + // case, `reply_audio.config` contains settings used to synthesize the + // speech. + // + // 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. + OutputAudio reply_audio = 2; + + // Only set if a Dialogflow automated agent has responded. + // Note that: [AutomatedAgentReply.detect_intent_response.output_audio][] + // and [AutomatedAgentReply.detect_intent_response.output_audio_config][] + // are always empty, use [reply_audio][google.cloud.dialogflow.v2.AnalyzeContentResponse.reply_audio] instead. + AutomatedAgentReply automated_agent_reply = 3; + + // Message analyzed by CCAI. + Message message = 5; + + // The suggestions for most recent human agent. The order is the same as + // [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] of + // [HumanAgentAssistantConfig.human_agent_suggestion_config][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.human_agent_suggestion_config]. + repeated SuggestionResult human_agent_suggestion_results = 6; + + // The suggestions for end user. The order is the same as + // [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] of + // [HumanAgentAssistantConfig.end_user_suggestion_config][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.end_user_suggestion_config]. + repeated SuggestionResult end_user_suggestion_results = 7; + + // Indicates the parameters of DTMF. + DtmfParameters dtmf_parameters = 9; +} + +// The top-level message sent by the client to the +// [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2.Participants.StreamingAnalyzeContent] method. +// +// Multiple request messages should be sent in order: +// +// 1. The first message must contain +// [participant][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.participant], +// [config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] and optionally +// [query_params][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.query_params]. If you want +// to receive an audio response, it should also contain +// [reply_audio_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.reply_audio_config]. +// The message must not contain +// [input][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input]. +// +// 2. If [config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] in the first message +// was set to [audio_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.audio_config], +// all subsequent messages must contain +// [input_audio][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input_audio] to continue +// with Speech recognition. +// However, note that: +// +// * Dialogflow will bill you for the audio so far. +// * Dialogflow discards all Speech recognition results in favor of the +// text input. +// +// 3. If [StreamingAnalyzeContentRequest.config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] in the first message was set +// to [StreamingAnalyzeContentRequest.text_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.text_config], then the second message +// must contain only [input_text][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input_text]. +// Moreover, you must not send more than two messages. +// +// After you sent all input, you must half-close or abort the request stream. +message StreamingAnalyzeContentRequest { + // Required. The name of the participant this text comes from. + // Format: `projects//locations//conversations//participants/`. + string participant = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Participant" + } + ]; + + // Required. The input config. + oneof config { + // Instructs the speech recognizer how to process the speech audio. + InputAudioConfig audio_config = 2; + + // The natural language text to be processed. + InputTextConfig text_config = 3; + } + + // Speech synthesis configuration. + // The speech synthesis settings for a virtual agent that may be configured + // for the associated conversation profile are not used when calling + // StreamingAnalyzeContent. If this configuration is not supplied, speech + // synthesis is disabled. + OutputAudioConfig reply_audio_config = 4; + + // Required. The input. + oneof input { + // The input audio content to be recognized. Must be sent if `audio_config` + // is set in the first message. The complete audio over all streaming + // messages must not exceed 1 minute. + bytes input_audio = 5; + + // The UTF-8 encoded natural language text to be processed. Must be sent if + // `text_config` is set in the first message. Text length must not exceed + // 256 bytes. The `input_text` field can be only sent once. + string input_text = 6; + + // The DTMF digits used to invoke intent and fill in parameter value. + // + // This input is ignored if the previous response indicated that DTMF input + // is not accepted. + TelephonyDtmfEvents input_dtmf = 9; + } + + // Parameters for a Dialogflow virtual-agent query. + QueryParameters query_params = 7; +} + +// The top-level message returned from the `StreamingAnalyzeContent` 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 next message contains `reply_text` and optionally `reply_audio` +// returned by an agent. This message may also contain +// `automated_agent_reply`. +message StreamingAnalyzeContentResponse { + // The result of speech recognition. + StreamingRecognitionResult recognition_result = 1; + + // The output text content. + // This field is set if an automated agent responded with a text for the user. + string reply_text = 2; + + // The audio data bytes encoded as specified in the request. + // This field is set if: + // + // - The `reply_audio_config` field is specified in the request. + // - The automated agent, which this output comes from, responded with audio. + // In such case, the `reply_audio.config` field contains settings used to + // synthesize the speech. + // + // 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. + OutputAudio reply_audio = 3; + + // Only set if a Dialogflow automated agent has responded. + // Note that: [AutomatedAgentReply.detect_intent_response.output_audio][] + // and [AutomatedAgentReply.detect_intent_response.output_audio_config][] + // are always empty, use [reply_audio][google.cloud.dialogflow.v2.StreamingAnalyzeContentResponse.reply_audio] instead. + AutomatedAgentReply automated_agent_reply = 4; + + // Message analyzed by CCAI. + Message message = 6; + + // The suggestions for most recent human agent. The order is the same as + // [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] of + // [HumanAgentAssistantConfig.human_agent_suggestion_config][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.human_agent_suggestion_config]. + repeated SuggestionResult human_agent_suggestion_results = 7; + + // The suggestions for end user. The order is the same as + // [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] of + // [HumanAgentAssistantConfig.end_user_suggestion_config][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.end_user_suggestion_config]. + repeated SuggestionResult end_user_suggestion_results = 8; + + // Indicates the parameters of DTMF. + DtmfParameters dtmf_parameters = 10; +} + +// The request message for [Participants.SuggestArticles][google.cloud.dialogflow.v2.Participants.SuggestArticles]. +message SuggestArticlesRequest { + // Required. The name of the participant to fetch suggestion for. + // Format: `projects//locations//conversations//participants/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Participant" + } + ]; + + // The name of the latest conversation message to compile suggestion + // for. If empty, it will be the latest message of the conversation. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2 [(google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Message" + }]; + + // Max number of messages prior to and including + // [latest_message][google.cloud.dialogflow.v2.SuggestArticlesRequest.latest_message] to use as context + // when compiling the suggestion. By default 20 and at most 50. + int32 context_size = 3; +} + +// The response message for [Participants.SuggestArticles][google.cloud.dialogflow.v2.Participants.SuggestArticles]. +message SuggestArticlesResponse { + // Articles ordered by score in descending order. + repeated ArticleAnswer article_answers = 1; + + // The name of the latest conversation message used to compile + // suggestion for. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2; + + // Number of messages prior to and including + // [latest_message][google.cloud.dialogflow.v2.SuggestArticlesResponse.latest_message] to compile the + // suggestion. It may be smaller than the + // [SuggestArticlesRequest.context_size][google.cloud.dialogflow.v2.SuggestArticlesRequest.context_size] field in the request if there + // aren't that many messages in the conversation. + int32 context_size = 3; +} + +// The request message for [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2.Participants.SuggestFaqAnswers]. +message SuggestFaqAnswersRequest { + // Required. The name of the participant to fetch suggestion for. + // Format: `projects//locations//conversations//participants/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Participant" + } + ]; + + // The name of the latest conversation message to compile suggestion + // for. If empty, it will be the latest message of the conversation. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2 [(google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Message" + }]; + + // Max number of messages prior to and including + // [latest_message] to use as context when compiling the + // suggestion. By default 20 and at most 50. + int32 context_size = 3; +} + +// The request message for [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2.Participants.SuggestFaqAnswers]. +message SuggestFaqAnswersResponse { + // Answers extracted from FAQ documents. + repeated FaqAnswer faq_answers = 1; + + // The name of the latest conversation message used to compile + // suggestion for. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2; + + // Number of messages prior to and including + // [latest_message][google.cloud.dialogflow.v2.SuggestFaqAnswersResponse.latest_message] to compile the + // suggestion. It may be smaller than the + // [SuggestFaqAnswersRequest.context_size][google.cloud.dialogflow.v2.SuggestFaqAnswersRequest.context_size] field in the request if there + // aren't that many messages in the conversation. + int32 context_size = 3; +} + +// Represents the natural language speech audio to be processed. +message AudioInput { + // Required. Instructs the speech recognizer how to process the speech audio. + InputAudioConfig config = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The natural language speech audio to be processed. + // A single request can contain up to 1 minute of speech audio data. + // The transcribed text cannot contain more than 256 bytes. + bytes audio = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// Represents the natural language speech audio to be played to the end user. +message OutputAudio { + // Instructs the speech synthesizer how to generate the speech + // audio. + OutputAudioConfig config = 1; + + // The natural language speech audio. + bytes audio = 2; +} + +// Represents a response from an automated agent. +message AutomatedAgentReply { + // Response of the Dialogflow [Sessions.DetectIntent][google.cloud.dialogflow.v2.Sessions.DetectIntent] call. + DetectIntentResponse detect_intent_response = 1; +} + +// Represents article answer. +message ArticleAnswer { + // The article title. + string title = 1; + + // The article URI. + string uri = 2; + + // Article snippets. + repeated string snippets = 3; + + // Article match confidence. + // The system's confidence score that this article is a good match for this + // conversation, as a value from 0.0 (completely uncertain) to 1.0 + // (completely certain). + float confidence = 4; + + // A map that contains metadata about the answer and the + // document from which it originates. + map metadata = 5; + + // The name of answer record, in the format of + // "projects//locations//answerRecords/" + string answer_record = 6; +} + +// Represents answer from "frequently asked questions". +message FaqAnswer { + // The piece of text from the `source` knowledge base document. + string answer = 1; + + // The system's confidence score that this Knowledge answer is a good match + // for this conversational query, range from 0.0 (completely uncertain) + // to 1.0 (completely certain). + float confidence = 2; + + // The corresponding FAQ question. + string question = 3; + + // Indicates which Knowledge Document this answer was extracted + // from. + // Format: `projects//locations//agent/knowledgeBases//documents/`. + string source = 4; + + // A map that contains metadata about the answer and the + // document from which it originates. + map metadata = 5; + + // The name of answer record, in the format of + // "projects//locations//answerRecords/" + string answer_record = 6; +} + +// One response of different type of suggestion response which is used in +// the response of [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent] and +// [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent], as well as [HumanAgentAssistantEvent][google.cloud.dialogflow.v2.HumanAgentAssistantEvent]. +message SuggestionResult { + // Different type of suggestion response. + oneof suggestion_response { + // Error status if the request failed. + google.rpc.Status error = 1; + + // SuggestArticlesResponse if request is for ARTICLE_SUGGESTION. + SuggestArticlesResponse suggest_articles_response = 2; + + // SuggestFaqAnswersResponse if request is for FAQ_ANSWER. + SuggestFaqAnswersResponse suggest_faq_answers_response = 3; + } +} + +// Defines the language used in the input text. +message InputTextConfig { + // Required. The language of this conversational query. See [Language + // Support](https://cloud.google.com/dialogflow/docs/reference/language) + // for a list of the currently supported language codes. + string language_code = 1 [(google.api.field_behavior) = REQUIRED]; +} + +// Represents a part of a message possibly annotated with an entity. The part +// can be an entity or purely a part of the message between two entities or +// message start/end. +message AnnotatedMessagePart { + // A part of a message possibly annotated with an entity. + string text = 1; + + // The [Dialogflow system entity + // type](https://cloud.google.com/dialogflow/docs/reference/system-entities) + // of this message part. If this is empty, Dialogflow could not annotate the + // phrase part with a system entity. + string entity_type = 2; + + // The [Dialogflow system entity formatted value + // ](https://cloud.google.com/dialogflow/docs/reference/system-entities) of + // this message part. For example for a system entity of type + // `@sys.unit-currency`, this may contain: + //
+  // {
+  //   "amount": 5,
+  //   "currency": "USD"
+  // }
+  // 
+ google.protobuf.Value formatted_value = 3; +} + +// Represents the result of annotation for the message. +message MessageAnnotation { + // The collection of annotated message parts ordered by their + // position in the message. You can recover the annotated message by + // concatenating [AnnotatedMessagePart.text]. + repeated AnnotatedMessagePart parts = 1; + + // Indicates whether the text message contains entities. + bool contain_entities = 2; +} diff --git a/google/cloud/dialogflow_v2/proto/session.proto b/google/cloud/dialogflow_v2/proto/session.proto index b99214a4c..338489652 100644 --- a/google/cloud/dialogflow_v2/proto/session.proto +++ b/google/cloud/dialogflow_v2/proto/session.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -80,8 +80,8 @@ service Sessions { // Note: Always use agent versions for production traffic. // See [Versions and // environments](https://cloud.google.com/dialogflow/es/docs/agents-versions). - rpc StreamingDetectIntent(stream StreamingDetectIntentRequest) - returns (stream StreamingDetectIntentResponse) {} + rpc StreamingDetectIntent(stream StreamingDetectIntentRequest) returns (stream StreamingDetectIntentResponse) { + } } // The request to detect user's intent. @@ -90,9 +90,10 @@ message DetectIntentRequest { // `projects//agent/sessions/`, or // `projects//agent/environments//users//sessions/`. If `Environment ID` is not specified, we assume - // default 'draft' environment. If `User ID` is not specified, we are using - // "-". It's up to the API caller to choose an appropriate `Session ID` and - // `User Id`. They can be a random number or some type of user and session + // default 'draft' environment (`Environment ID` might be referred to as + // environment name at some places). If `User ID` is not specified, we are + // using "-". It's up to the API caller to choose an appropriate `Session ID` + // and `User Id`. They can be a random number or some type of user and session // identifiers (preferably hashed). The length of the `Session ID` and // `User ID` must not exceed 36 characters. // @@ -127,14 +128,12 @@ message DetectIntentRequest { // configured, no output audio is generated. OutputAudioConfig output_audio_config = 4; - // Mask for - // [output_audio_config][google.cloud.dialogflow.v2.DetectIntentRequest.output_audio_config] - // indicating which settings in this request-level config should override - // speech synthesizer settings defined at agent-level. + // Mask for [output_audio_config][google.cloud.dialogflow.v2.DetectIntentRequest.output_audio_config] indicating which settings in this + // request-level config should override speech synthesizer settings defined at + // agent-level. // - // If unspecified or empty, - // [output_audio_config][google.cloud.dialogflow.v2.DetectIntentRequest.output_audio_config] - // replaces the agent-level config in its entirety. + // If unspecified or empty, [output_audio_config][google.cloud.dialogflow.v2.DetectIntentRequest.output_audio_config] replaces the agent-level + // config in its entirety. google.protobuf.FieldMask output_audio_config_mask = 7; // The natural language speech audio to be processed. This field @@ -284,7 +283,8 @@ message QueryResult { // - 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 + // - Else: depending on parameter value type, could be one of string, + // number, boolean, null, list or map // - MapValue value: // - If parameter's entity type is a composite entity: // map from composite entity property names to property values @@ -350,29 +350,25 @@ message QueryResult { } // The top-level message sent by the client to the -// [Sessions.StreamingDetectIntent][google.cloud.dialogflow.v2.Sessions.StreamingDetectIntent] -// method. +// [Sessions.StreamingDetectIntent][google.cloud.dialogflow.v2.Sessions.StreamingDetectIntent] method. // // Multiple request messages should be sent in order: // // 1. The first message must contain // [session][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.session], -// [query_input][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.query_input] -// plus optionally -// [query_params][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.query_params]. -// If the client wants to receive an audio response, it should also contain +// [query_input][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.query_input] plus optionally +// [query_params][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.query_params]. If the client +// wants to receive an audio response, it should also contain // [output_audio_config][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.output_audio_config]. // The message must not contain // [input_audio][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.input_audio]. -// 2. If -// [query_input][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.query_input] -// was set to -// [query_input.audio_config][google.cloud.dialogflow.v2.InputAudioConfig], -// all subsequent messages must contain -// [input_audio][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.input_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 +// 2. If [query_input][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.query_input] was set to +// [query_input.audio_config][google.cloud.dialogflow.v2.InputAudioConfig], all subsequent +// messages must contain +// [input_audio][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.input_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.v2.QueryInput.text]. // // However, note that: @@ -421,15 +417,14 @@ message StreamingDetectIntentRequest { // 3. an event that specifies which intent to trigger. QueryInput query_input = 3 [(google.api.field_behavior) = REQUIRED]; - // Please use - // [InputAudioConfig.single_utterance][google.cloud.dialogflow.v2.InputAudioConfig.single_utterance] - // instead. 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. This setting is ignored when `query_input` is a piece - // of text or an event. + // Please use [InputAudioConfig.single_utterance][google.cloud.dialogflow.v2.InputAudioConfig.single_utterance] instead. + // 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. + // This setting is ignored when `query_input` is a piece of text or an event. bool single_utterance = 4 [deprecated = true]; // Instructs the speech synthesizer how to generate the output @@ -437,14 +432,12 @@ message StreamingDetectIntentRequest { // configured, no output audio is generated. OutputAudioConfig output_audio_config = 5; - // Mask for - // [output_audio_config][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.output_audio_config] - // indicating which settings in this request-level config should override - // speech synthesizer settings defined at agent-level. + // Mask for [output_audio_config][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.output_audio_config] indicating which settings in this + // request-level config should override speech synthesizer settings defined at + // agent-level. // - // If unspecified or empty, - // [output_audio_config][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.output_audio_config] - // replaces the agent-level config in its entirety. + // If unspecified or empty, [output_audio_config][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.output_audio_config] replaces the agent-level + // config in its entirety. google.protobuf.FieldMask output_audio_config_mask = 7; // The input audio content to be recognized. Must be sent if @@ -539,12 +532,11 @@ message StreamingRecognitionResult { // Event indicates that the server has detected the end of the user's speech // utterance and expects no additional inputs. - // Therefore, the server will not process additional audio (although it may - // subsequently return additional results). The client should stop sending - // additional audio data, half-close the gRPC connection, and wait for any - // additional results until the server closes the gRPC connection. This - // message is only sent if `single_utterance` was set to `true`, and is not - // used otherwise. + // Therefore, the server will not process additional audio (although it may subsequently return additional results). The + // client should stop sending additional audio data, half-close the gRPC + // connection, and wait for any additional results until the server closes + // the gRPC connection. This message is only sent if `single_utterance` was + // set to `true`, and is not used otherwise. END_OF_SINGLE_UTTERANCE = 2; } @@ -571,8 +563,7 @@ message StreamingRecognitionResult { float confidence = 4; // Word-specific information for the words recognized by Speech in - // [transcript][google.cloud.dialogflow.v2.StreamingRecognitionResult.transcript]. - // Populated if and only if `message_type` = `TRANSCRIPT` and + // [transcript][google.cloud.dialogflow.v2.StreamingRecognitionResult.transcript]. Populated if and only if `message_type` = `TRANSCRIPT` and // [InputAudioConfig.enable_word_info] is set. repeated SpeechWordInfo speech_word_info = 7; @@ -613,7 +604,8 @@ message EventInput { // - 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 + // - Else: depending on parameter value type, could be one of string, + // number, boolean, null, list or map // - MapValue value: // - If parameter's entity type is a composite entity: // map from composite entity property names to property values @@ -639,14 +631,11 @@ message SentimentAnalysisRequestConfig { // and identifies the prevailing subjective opinion, especially to determine a // user's attitude as positive, negative, or neutral. // For [Participants.DetectIntent][], it needs to be configured in -// [DetectIntentRequest.query_params][google.cloud.dialogflow.v2.DetectIntentRequest.query_params]. -// For [Participants.StreamingDetectIntent][], it needs to be configured in +// [DetectIntentRequest.query_params][google.cloud.dialogflow.v2.DetectIntentRequest.query_params]. For +// [Participants.StreamingDetectIntent][], it needs to be configured in // [StreamingDetectIntentRequest.query_params][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.query_params]. -// And for -// [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent] -// and -// [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2.Participants.StreamingAnalyzeContent], -// it needs to be configured in +// And for [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent] and +// [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2.Participants.StreamingAnalyzeContent], it needs to be configured in // [ConversationProfile.human_agent_assistant_config][google.cloud.dialogflow.v2.ConversationProfile.human_agent_assistant_config] message SentimentAnalysisResult { // The sentiment analysis result for `query_text`. diff --git a/google/cloud/dialogflow_v2/proto/session_entity_type.proto b/google/cloud/dialogflow_v2/proto/session_entity_type.proto index 8d7fa89c0..0eceed81c 100644 --- a/google/cloud/dialogflow_v2/proto/session_entity_type.proto +++ b/google/cloud/dialogflow_v2/proto/session_entity_type.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/google/cloud/dialogflow_v2/proto/validation_result.proto b/google/cloud/dialogflow_v2/proto/validation_result.proto index e499c1462..e41cb9cde 100644 --- a/google/cloud/dialogflow_v2/proto/validation_result.proto +++ b/google/cloud/dialogflow_v2/proto/validation_result.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ message ValidationError { // Not specified. This value should never be used. SEVERITY_UNSPECIFIED = 0; - // The agent doesn't follow Dialogflow best practicies. + // The agent doesn't follow Dialogflow best practices. INFO = 1; // The agent may not behave as expected. diff --git a/google/cloud/dialogflow_v2/proto/webhook.proto b/google/cloud/dialogflow_v2/proto/webhook.proto index 19f529aed..ce72e92d3 100644 --- a/google/cloud/dialogflow_v2/proto/webhook.proto +++ b/google/cloud/dialogflow_v2/proto/webhook.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/google/cloud/dialogflow_v2/services/agents/async_client.py b/google/cloud/dialogflow_v2/services/agents/async_client.py index a108ea61c..6ebcfe0ba 100644 --- a/google/cloud/dialogflow_v2/services/agents/async_client.py +++ b/google/cloud/dialogflow_v2/services/agents/async_client.py @@ -72,7 +72,36 @@ class AgentsAsyncClient: 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 + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AgentsAsyncClient: The constructed client. + """ + return AgentsClient.from_service_account_info.__func__(AgentsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AgentsAsyncClient: The constructed client. + """ + return AgentsClient.from_service_account_file.__func__(AgentsAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -148,12 +177,13 @@ async def get_agent( r"""Retrieves the specified agent. Args: - request (:class:`~.agent.GetAgentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.GetAgentRequest`): The request object. The request message for [Agents.GetAgent][google.cloud.dialogflow.v2.Agents.GetAgent]. parent (:class:`str`): Required. The project that the agent to fetch is associated with. Format: ``projects/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -165,19 +195,18 @@ async def get_agent( sent along with the request as metadata. Returns: - ~.agent.Agent: - A Dialogflow agent is a virtual agent that handles - conversations with your end-users. It is a natural - language understanding module that understands the - nuances of human language. Dialogflow translates - end-user text or audio during a conversation to - structured data that your apps and services can - understand. You design and build a Dialogflow agent to - handle the types of conversations required for your - system. - - For more information about agents, see the `Agent - guide `__. + google.cloud.dialogflow_v2.types.Agent: + A Dialogflow agent is a virtual agent that handles conversations with your + end-users. It is a natural language understanding + module that understands the nuances of human + language. Dialogflow translates end-user text or + audio during a conversation to structured data that + your apps and services can understand. You design and + build a Dialogflow agent to handle the types of + conversations required for your system. + + For more information about agents, see the [Agent + guide](\ https://cloud.google.com/dialogflow/docs/agents-overview). """ # Create or coerce a protobuf request object. @@ -230,10 +259,10 @@ async def set_agent( r"""Creates/updates the specified agent. Args: - request (:class:`~.gcd_agent.SetAgentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.SetAgentRequest`): The request object. The request message for [Agents.SetAgent][google.cloud.dialogflow.v2.Agents.SetAgent]. - agent (:class:`~.gcd_agent.Agent`): + agent (:class:`google.cloud.dialogflow_v2.types.Agent`): Required. The agent to update. This corresponds to the ``agent`` field on the ``request`` instance; if ``request`` is provided, this @@ -246,19 +275,18 @@ async def set_agent( sent along with the request as metadata. Returns: - ~.gcd_agent.Agent: - A Dialogflow agent is a virtual agent that handles - conversations with your end-users. It is a natural - language understanding module that understands the - nuances of human language. Dialogflow translates - end-user text or audio during a conversation to - structured data that your apps and services can - understand. You design and build a Dialogflow agent to - handle the types of conversations required for your - system. - - For more information about agents, see the `Agent - guide `__. + google.cloud.dialogflow_v2.types.Agent: + A Dialogflow agent is a virtual agent that handles conversations with your + end-users. It is a natural language understanding + module that understands the nuances of human + language. Dialogflow translates end-user text or + audio during a conversation to structured data that + your apps and services can understand. You design and + build a Dialogflow agent to handle the types of + conversations required for your system. + + For more information about agents, see the [Agent + guide](\ https://cloud.google.com/dialogflow/docs/agents-overview). """ # Create or coerce a protobuf request object. @@ -313,12 +341,13 @@ async def delete_agent( r"""Deletes the specified agent. Args: - request (:class:`~.agent.DeleteAgentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.DeleteAgentRequest`): The request object. The request message for [Agents.DeleteAgent][google.cloud.dialogflow.v2.Agents.DeleteAgent]. parent (:class:`str`): Required. The project that the agent to delete is associated with. Format: ``projects/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -384,12 +413,13 @@ async def search_agents( Sub-Collections `__. Args: - request (:class:`~.agent.SearchAgentsRequest`): + request (:class:`google.cloud.dialogflow_v2.types.SearchAgentsRequest`): The request object. The request message for [Agents.SearchAgents][google.cloud.dialogflow.v2.Agents.SearchAgents]. parent (:class:`str`): Required. The project to list agents from. Format: ``projects/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -401,7 +431,7 @@ async def search_agents( sent along with the request as metadata. Returns: - ~.pagers.SearchAgentsAsyncPager: + google.cloud.dialogflow_v2.services.agents.pagers.SearchAgentsAsyncPager: The response message for [Agents.SearchAgents][google.cloud.dialogflow.v2.Agents.SearchAgents]. @@ -468,12 +498,13 @@ async def train_agent( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.agent.TrainAgentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.TrainAgentRequest`): The request object. The request message for [Agents.TrainAgent][google.cloud.dialogflow.v2.Agents.TrainAgent]. parent (:class:`str`): Required. The project that the agent to train is associated with. Format: ``projects/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -485,24 +516,22 @@ async def train_agent( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -566,12 +595,13 @@ async def export_agent( [ExportAgentResponse][google.cloud.dialogflow.v2.ExportAgentResponse]> Args: - request (:class:`~.agent.ExportAgentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.ExportAgentRequest`): The request object. The request message for [Agents.ExportAgent][google.cloud.dialogflow.v2.Agents.ExportAgent]. parent (:class:`str`): Required. The project that the agent to export is associated with. Format: ``projects/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -583,12 +613,12 @@ async def export_agent( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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 + :class:`google.cloud.dialogflow_v2.types.ExportAgentResponse` + The response message for [Agents.ExportAgent][google.cloud.dialogflow.v2.Agents.ExportAgent]. """ @@ -665,7 +695,7 @@ async def import_agent( draft agent is updated not when it is done training. Args: - request (:class:`~.agent.ImportAgentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.ImportAgentRequest`): The request object. The request message for [Agents.ImportAgent][google.cloud.dialogflow.v2.Agents.ImportAgent]. @@ -676,24 +706,22 @@ async def import_agent( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -753,7 +781,7 @@ async def restore_agent( draft agent is updated not when it is done training. Args: - request (:class:`~.agent.RestoreAgentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.RestoreAgentRequest`): The request object. The request message for [Agents.RestoreAgent][google.cloud.dialogflow.v2.Agents.RestoreAgent]. @@ -764,24 +792,22 @@ async def restore_agent( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -829,7 +855,7 @@ async def get_validation_result( automatically when training is completed. Args: - request (:class:`~.agent.GetValidationResultRequest`): + request (:class:`google.cloud.dialogflow_v2.types.GetValidationResultRequest`): The request object. The request message for [Agents.GetValidationResult][google.cloud.dialogflow.v2.Agents.GetValidationResult]. @@ -840,7 +866,7 @@ async def get_validation_result( sent along with the request as metadata. Returns: - ~.validation_result.ValidationResult: + google.cloud.dialogflow_v2.types.ValidationResult: Represents the output of agent validation. diff --git a/google/cloud/dialogflow_v2/services/agents/client.py b/google/cloud/dialogflow_v2/services/agents/client.py index f57fa7c1b..56cd648d7 100644 --- a/google/cloud/dialogflow_v2/services/agents/client.py +++ b/google/cloud/dialogflow_v2/services/agents/client.py @@ -114,6 +114,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AgentsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -126,7 +142,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + AgentsClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -229,10 +245,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.AgentsTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -268,21 +284,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -325,7 +337,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -342,12 +354,13 @@ def get_agent( r"""Retrieves the specified agent. Args: - request (:class:`~.agent.GetAgentRequest`): + request (google.cloud.dialogflow_v2.types.GetAgentRequest): The request object. The request message for [Agents.GetAgent][google.cloud.dialogflow.v2.Agents.GetAgent]. - parent (:class:`str`): + parent (str): Required. The project that the agent to fetch is associated with. Format: ``projects/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -359,19 +372,18 @@ def get_agent( sent along with the request as metadata. Returns: - ~.agent.Agent: - A Dialogflow agent is a virtual agent that handles - conversations with your end-users. It is a natural - language understanding module that understands the - nuances of human language. Dialogflow translates - end-user text or audio during a conversation to - structured data that your apps and services can - understand. You design and build a Dialogflow agent to - handle the types of conversations required for your - system. - - For more information about agents, see the `Agent - guide `__. + google.cloud.dialogflow_v2.types.Agent: + A Dialogflow agent is a virtual agent that handles conversations with your + end-users. It is a natural language understanding + module that understands the nuances of human + language. Dialogflow translates end-user text or + audio during a conversation to structured data that + your apps and services can understand. You design and + build a Dialogflow agent to handle the types of + conversations required for your system. + + For more information about agents, see the [Agent + guide](\ https://cloud.google.com/dialogflow/docs/agents-overview). """ # Create or coerce a protobuf request object. @@ -425,10 +437,10 @@ def set_agent( r"""Creates/updates the specified agent. Args: - request (:class:`~.gcd_agent.SetAgentRequest`): + request (google.cloud.dialogflow_v2.types.SetAgentRequest): The request object. The request message for [Agents.SetAgent][google.cloud.dialogflow.v2.Agents.SetAgent]. - agent (:class:`~.gcd_agent.Agent`): + agent (google.cloud.dialogflow_v2.types.Agent): Required. The agent to update. This corresponds to the ``agent`` field on the ``request`` instance; if ``request`` is provided, this @@ -441,19 +453,18 @@ def set_agent( sent along with the request as metadata. Returns: - ~.gcd_agent.Agent: - A Dialogflow agent is a virtual agent that handles - conversations with your end-users. It is a natural - language understanding module that understands the - nuances of human language. Dialogflow translates - end-user text or audio during a conversation to - structured data that your apps and services can - understand. You design and build a Dialogflow agent to - handle the types of conversations required for your - system. - - For more information about agents, see the `Agent - guide `__. + google.cloud.dialogflow_v2.types.Agent: + A Dialogflow agent is a virtual agent that handles conversations with your + end-users. It is a natural language understanding + module that understands the nuances of human + language. Dialogflow translates end-user text or + audio during a conversation to structured data that + your apps and services can understand. You design and + build a Dialogflow agent to handle the types of + conversations required for your system. + + For more information about agents, see the [Agent + guide](\ https://cloud.google.com/dialogflow/docs/agents-overview). """ # Create or coerce a protobuf request object. @@ -509,12 +520,13 @@ def delete_agent( r"""Deletes the specified agent. Args: - request (:class:`~.agent.DeleteAgentRequest`): + request (google.cloud.dialogflow_v2.types.DeleteAgentRequest): The request object. The request message for [Agents.DeleteAgent][google.cloud.dialogflow.v2.Agents.DeleteAgent]. - parent (:class:`str`): + parent (str): Required. The project that the agent to delete is associated with. Format: ``projects/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -581,12 +593,13 @@ def search_agents( Sub-Collections `__. Args: - request (:class:`~.agent.SearchAgentsRequest`): + request (google.cloud.dialogflow_v2.types.SearchAgentsRequest): The request object. The request message for [Agents.SearchAgents][google.cloud.dialogflow.v2.Agents.SearchAgents]. - parent (:class:`str`): + parent (str): Required. The project to list agents from. Format: ``projects/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -598,7 +611,7 @@ def search_agents( sent along with the request as metadata. Returns: - ~.pagers.SearchAgentsPager: + google.cloud.dialogflow_v2.services.agents.pagers.SearchAgentsPager: The response message for [Agents.SearchAgents][google.cloud.dialogflow.v2.Agents.SearchAgents]. @@ -666,12 +679,13 @@ def train_agent( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.agent.TrainAgentRequest`): + request (google.cloud.dialogflow_v2.types.TrainAgentRequest): The request object. The request message for [Agents.TrainAgent][google.cloud.dialogflow.v2.Agents.TrainAgent]. - parent (:class:`str`): + parent (str): Required. The project that the agent to train is associated with. Format: ``projects/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -683,24 +697,22 @@ def train_agent( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -765,12 +777,13 @@ def export_agent( [ExportAgentResponse][google.cloud.dialogflow.v2.ExportAgentResponse]> Args: - request (:class:`~.agent.ExportAgentRequest`): + request (google.cloud.dialogflow_v2.types.ExportAgentRequest): The request object. The request message for [Agents.ExportAgent][google.cloud.dialogflow.v2.Agents.ExportAgent]. - parent (:class:`str`): + parent (str): Required. The project that the agent to export is associated with. Format: ``projects/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -782,12 +795,12 @@ def export_agent( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.agent.ExportAgentResponse``: The response - message for + :class:`google.cloud.dialogflow_v2.types.ExportAgentResponse` + The response message for [Agents.ExportAgent][google.cloud.dialogflow.v2.Agents.ExportAgent]. """ @@ -865,7 +878,7 @@ def import_agent( draft agent is updated not when it is done training. Args: - request (:class:`~.agent.ImportAgentRequest`): + request (google.cloud.dialogflow_v2.types.ImportAgentRequest): The request object. The request message for [Agents.ImportAgent][google.cloud.dialogflow.v2.Agents.ImportAgent]. @@ -876,24 +889,22 @@ def import_agent( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -954,7 +965,7 @@ def restore_agent( draft agent is updated not when it is done training. Args: - request (:class:`~.agent.RestoreAgentRequest`): + request (google.cloud.dialogflow_v2.types.RestoreAgentRequest): The request object. The request message for [Agents.RestoreAgent][google.cloud.dialogflow.v2.Agents.RestoreAgent]. @@ -965,24 +976,22 @@ def restore_agent( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1031,7 +1040,7 @@ def get_validation_result( automatically when training is completed. Args: - request (:class:`~.agent.GetValidationResultRequest`): + request (google.cloud.dialogflow_v2.types.GetValidationResultRequest): The request object. The request message for [Agents.GetValidationResult][google.cloud.dialogflow.v2.Agents.GetValidationResult]. @@ -1042,7 +1051,7 @@ def get_validation_result( sent along with the request as metadata. Returns: - ~.validation_result.ValidationResult: + google.cloud.dialogflow_v2.types.ValidationResult: Represents the output of agent validation. diff --git a/google/cloud/dialogflow_v2/services/agents/pagers.py b/google/cloud/dialogflow_v2/services/agents/pagers.py index a05bff566..edb50660a 100644 --- a/google/cloud/dialogflow_v2/services/agents/pagers.py +++ b/google/cloud/dialogflow_v2/services/agents/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2.types import agent @@ -24,7 +33,7 @@ class SearchAgentsPager: """A pager for iterating through ``search_agents`` requests. This class thinly wraps an initial - :class:`~.agent.SearchAgentsResponse` object, and + :class:`google.cloud.dialogflow_v2.types.SearchAgentsResponse` object, and provides an ``__iter__`` method to iterate through its ``agents`` field. @@ -33,7 +42,7 @@ class SearchAgentsPager: through the ``agents`` field on the corresponding responses. - All the usual :class:`~.agent.SearchAgentsResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.SearchAgentsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.agent.SearchAgentsRequest`): + request (google.cloud.dialogflow_v2.types.SearchAgentsRequest): The initial request object. - response (:class:`~.agent.SearchAgentsResponse`): + response (google.cloud.dialogflow_v2.types.SearchAgentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class SearchAgentsAsyncPager: """A pager for iterating through ``search_agents`` requests. This class thinly wraps an initial - :class:`~.agent.SearchAgentsResponse` object, and + :class:`google.cloud.dialogflow_v2.types.SearchAgentsResponse` object, and provides an ``__aiter__`` method to iterate through its ``agents`` field. @@ -95,7 +104,7 @@ class SearchAgentsAsyncPager: through the ``agents`` field on the corresponding responses. - All the usual :class:`~.agent.SearchAgentsResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.SearchAgentsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.agent.SearchAgentsRequest`): + request (google.cloud.dialogflow_v2.types.SearchAgentsRequest): The initial request object. - response (:class:`~.agent.SearchAgentsResponse`): + response (google.cloud.dialogflow_v2.types.SearchAgentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2/services/agents/transports/grpc.py b/google/cloud/dialogflow_v2/services/agents/transports/grpc.py index fdc5fe6e5..ff1f4bb3f 100644 --- a/google/cloud/dialogflow_v2/services/agents/transports/grpc.py +++ b/google/cloud/dialogflow_v2/services/agents/transports/grpc.py @@ -62,6 +62,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -92,6 +93,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -108,6 +113,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -117,11 +127,6 @@ def __init__( 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 @@ -165,12 +170,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/agents/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/agents/transports/grpc_asyncio.py index 0b453bacb..1bb87d75c 100644 --- a/google/cloud/dialogflow_v2/services/agents/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2/services/agents/transports/grpc_asyncio.py @@ -106,6 +106,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -137,6 +138,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -153,6 +158,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -162,11 +172,6 @@ def __init__( 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 @@ -210,12 +215,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/answer_records/__init__.py b/google/cloud/dialogflow_v2/services/answer_records/__init__.py new file mode 100644 index 000000000..7618f55a7 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/answer_records/__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 AnswerRecordsClient +from .async_client import AnswerRecordsAsyncClient + +__all__ = ( + "AnswerRecordsClient", + "AnswerRecordsAsyncClient", +) diff --git a/google/cloud/dialogflow_v2/services/answer_records/async_client.py b/google/cloud/dialogflow_v2/services/answer_records/async_client.py new file mode 100644 index 000000000..aad1111e5 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/answer_records/async_client.py @@ -0,0 +1,387 @@ +# -*- 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.dialogflow_v2.services.answer_records import pagers +from google.cloud.dialogflow_v2.types import answer_record +from google.cloud.dialogflow_v2.types import answer_record as gcd_answer_record +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import AnswerRecordsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import AnswerRecordsGrpcAsyncIOTransport +from .client import AnswerRecordsClient + + +class AnswerRecordsAsyncClient: + """Service for managing + [AnswerRecords][google.cloud.dialogflow.v2.AnswerRecord]. + """ + + _client: AnswerRecordsClient + + DEFAULT_ENDPOINT = AnswerRecordsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = AnswerRecordsClient.DEFAULT_MTLS_ENDPOINT + + answer_record_path = staticmethod(AnswerRecordsClient.answer_record_path) + parse_answer_record_path = staticmethod( + AnswerRecordsClient.parse_answer_record_path + ) + + common_billing_account_path = staticmethod( + AnswerRecordsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + AnswerRecordsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(AnswerRecordsClient.common_folder_path) + parse_common_folder_path = staticmethod( + AnswerRecordsClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + AnswerRecordsClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + AnswerRecordsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(AnswerRecordsClient.common_project_path) + parse_common_project_path = staticmethod( + AnswerRecordsClient.parse_common_project_path + ) + + common_location_path = staticmethod(AnswerRecordsClient.common_location_path) + parse_common_location_path = staticmethod( + AnswerRecordsClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AnswerRecordsAsyncClient: The constructed client. + """ + return AnswerRecordsClient.from_service_account_info.__func__(AnswerRecordsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AnswerRecordsAsyncClient: The constructed client. + """ + return AnswerRecordsClient.from_service_account_file.__func__(AnswerRecordsAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> AnswerRecordsTransport: + """Return the transport used by the client instance. + + Returns: + AnswerRecordsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(AnswerRecordsClient).get_transport_class, type(AnswerRecordsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, AnswerRecordsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the answer records 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, ~.AnswerRecordsTransport]): 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 = AnswerRecordsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_answer_records( + self, + request: answer_record.ListAnswerRecordsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListAnswerRecordsAsyncPager: + r"""Returns the list of all answer records in the + specified project in reverse chronological order. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.ListAnswerRecordsRequest`): + The request object. Request message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2.AnswerRecords.ListAnswerRecords]. + parent (:class:`str`): + Required. The project to list all answer records for in + reverse chronological order. 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: + google.cloud.dialogflow_v2.services.answer_records.pagers.ListAnswerRecordsAsyncPager: + Response message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2.AnswerRecords.ListAnswerRecords]. + + 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 = answer_record.ListAnswerRecordsRequest(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_answer_records, + 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.ListAnswerRecordsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def update_answer_record( + self, + request: gcd_answer_record.UpdateAnswerRecordRequest = None, + *, + answer_record: gcd_answer_record.AnswerRecord = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_answer_record.AnswerRecord: + r"""Updates the specified answer record. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.UpdateAnswerRecordRequest`): + The request object. Request message for + [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2.AnswerRecords.UpdateAnswerRecord]. + answer_record (:class:`google.cloud.dialogflow_v2.types.AnswerRecord`): + Required. Answer record to update. + This corresponds to the ``answer_record`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.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: + google.cloud.dialogflow_v2.types.AnswerRecord: + Answer records are records to manage answer history and feedbacks for + Dialogflow. + + Currently, answer record includes: + + - human agent assistant article suggestion + - human agent assistant faq article + + It doesn't include: + + - DetectIntent intent matching + - DetectIntent knowledge + + Answer records are not related to the conversation + history in the Dialogflow Console. A Record is + generated even when the end-user disables + conversation history in the console. Records are + created when there's a human agent assistant + suggestion generated. + + A typical workflow for customers provide feedback to + an answer is: + + 1. For human agent assistant, customers get + suggestion via ListSuggestions API. Together with + the answers, + [AnswerRecord.name][google.cloud.dialogflow.v2.AnswerRecord.name] + are returned to the customers. + 2. The customer uses the + [AnswerRecord.name][google.cloud.dialogflow.v2.AnswerRecord.name] + to call the [UpdateAnswerRecord][] method to send + feedback about a specific answer that they believe + is wrong. + + """ + # Create or coerce a 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([answer_record, 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 = gcd_answer_record.UpdateAnswerRecordRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if answer_record is not None: + request.answer_record = answer_record + 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_answer_record, + 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( + (("answer_record.name", request.answer_record.name),) + ), + ) + + # 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("AnswerRecordsAsyncClient",) diff --git a/google/cloud/dialogflow_v2/services/answer_records/client.py b/google/cloud/dialogflow_v2/services/answer_records/client.py new file mode 100644 index 000000000..3a04afa04 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/answer_records/client.py @@ -0,0 +1,558 @@ +# -*- 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.dialogflow_v2.services.answer_records import pagers +from google.cloud.dialogflow_v2.types import answer_record +from google.cloud.dialogflow_v2.types import answer_record as gcd_answer_record +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import AnswerRecordsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import AnswerRecordsGrpcTransport +from .transports.grpc_asyncio import AnswerRecordsGrpcAsyncIOTransport + + +class AnswerRecordsClientMeta(type): + """Metaclass for the AnswerRecords 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[AnswerRecordsTransport]] + _transport_registry["grpc"] = AnswerRecordsGrpcTransport + _transport_registry["grpc_asyncio"] = AnswerRecordsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[AnswerRecordsTransport]: + """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 AnswerRecordsClient(metaclass=AnswerRecordsClientMeta): + """Service for managing + [AnswerRecords][google.cloud.dialogflow.v2.AnswerRecord]. + """ + + @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_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AnswerRecordsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AnswerRecordsClient: 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) -> AnswerRecordsTransport: + """Return the transport used by the client instance. + + Returns: + AnswerRecordsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def answer_record_path(project: str, answer_record: str,) -> str: + """Return a fully-qualified answer_record string.""" + return "projects/{project}/answerRecords/{answer_record}".format( + project=project, answer_record=answer_record, + ) + + @staticmethod + def parse_answer_record_path(path: str) -> Dict[str, str]: + """Parse a answer_record path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/answerRecords/(?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, AnswerRecordsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the answer records 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, AnswerRecordsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, AnswerRecordsTransport): + # transport is a AnswerRecordsTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_answer_records( + self, + request: answer_record.ListAnswerRecordsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListAnswerRecordsPager: + r"""Returns the list of all answer records in the + specified project in reverse chronological order. + + Args: + request (google.cloud.dialogflow_v2.types.ListAnswerRecordsRequest): + The request object. Request message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2.AnswerRecords.ListAnswerRecords]. + parent (str): + Required. The project to list all answer records for in + reverse chronological order. 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: + google.cloud.dialogflow_v2.services.answer_records.pagers.ListAnswerRecordsPager: + Response message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2.AnswerRecords.ListAnswerRecords]. + + 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 answer_record.ListAnswerRecordsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, answer_record.ListAnswerRecordsRequest): + request = answer_record.ListAnswerRecordsRequest(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_answer_records] + + # 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.ListAnswerRecordsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def update_answer_record( + self, + request: gcd_answer_record.UpdateAnswerRecordRequest = None, + *, + answer_record: gcd_answer_record.AnswerRecord = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_answer_record.AnswerRecord: + r"""Updates the specified answer record. + + Args: + request (google.cloud.dialogflow_v2.types.UpdateAnswerRecordRequest): + The request object. Request message for + [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2.AnswerRecords.UpdateAnswerRecord]. + answer_record (google.cloud.dialogflow_v2.types.AnswerRecord): + Required. Answer record to update. + This corresponds to the ``answer_record`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.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: + google.cloud.dialogflow_v2.types.AnswerRecord: + Answer records are records to manage answer history and feedbacks for + Dialogflow. + + Currently, answer record includes: + + - human agent assistant article suggestion + - human agent assistant faq article + + It doesn't include: + + - DetectIntent intent matching + - DetectIntent knowledge + + Answer records are not related to the conversation + history in the Dialogflow Console. A Record is + generated even when the end-user disables + conversation history in the console. Records are + created when there's a human agent assistant + suggestion generated. + + A typical workflow for customers provide feedback to + an answer is: + + 1. For human agent assistant, customers get + suggestion via ListSuggestions API. Together with + the answers, + [AnswerRecord.name][google.cloud.dialogflow.v2.AnswerRecord.name] + are returned to the customers. + 2. The customer uses the + [AnswerRecord.name][google.cloud.dialogflow.v2.AnswerRecord.name] + to call the [UpdateAnswerRecord][] method to send + feedback about a specific answer that they believe + is wrong. + + """ + # Create or coerce a 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([answer_record, 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 gcd_answer_record.UpdateAnswerRecordRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_answer_record.UpdateAnswerRecordRequest): + request = gcd_answer_record.UpdateAnswerRecordRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if answer_record is not None: + request.answer_record = answer_record + 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_answer_record] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("answer_record.name", request.answer_record.name),) + ), + ) + + # 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("AnswerRecordsClient",) diff --git a/google/cloud/dialogflow_v2/services/answer_records/pagers.py b/google/cloud/dialogflow_v2/services/answer_records/pagers.py new file mode 100644 index 000000000..6495fe2db --- /dev/null +++ b/google/cloud/dialogflow_v2/services/answer_records/pagers.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.dialogflow_v2.types import answer_record + + +class ListAnswerRecordsPager: + """A pager for iterating through ``list_answer_records`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListAnswerRecordsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``answer_records`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListAnswerRecords`` requests and continue to iterate + through the ``answer_records`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListAnswerRecordsResponse` + 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[..., answer_record.ListAnswerRecordsResponse], + request: answer_record.ListAnswerRecordsRequest, + response: answer_record.ListAnswerRecordsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListAnswerRecordsRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListAnswerRecordsResponse): + 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 = answer_record.ListAnswerRecordsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[answer_record.ListAnswerRecordsResponse]: + 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[answer_record.AnswerRecord]: + for page in self.pages: + yield from page.answer_records + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListAnswerRecordsAsyncPager: + """A pager for iterating through ``list_answer_records`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListAnswerRecordsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``answer_records`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListAnswerRecords`` requests and continue to iterate + through the ``answer_records`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListAnswerRecordsResponse` + 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[answer_record.ListAnswerRecordsResponse]], + request: answer_record.ListAnswerRecordsRequest, + response: answer_record.ListAnswerRecordsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListAnswerRecordsRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListAnswerRecordsResponse): + 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 = answer_record.ListAnswerRecordsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[answer_record.ListAnswerRecordsResponse]: + 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[answer_record.AnswerRecord]: + async def async_generator(): + async for page in self.pages: + for response in page.answer_records: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflow_v2/services/answer_records/transports/__init__.py b/google/cloud/dialogflow_v2/services/answer_records/transports/__init__.py new file mode 100644 index 000000000..bbb65cf15 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/answer_records/transports/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import AnswerRecordsTransport +from .grpc import AnswerRecordsGrpcTransport +from .grpc_asyncio import AnswerRecordsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[AnswerRecordsTransport]] +_transport_registry["grpc"] = AnswerRecordsGrpcTransport +_transport_registry["grpc_asyncio"] = AnswerRecordsGrpcAsyncIOTransport + +__all__ = ( + "AnswerRecordsTransport", + "AnswerRecordsGrpcTransport", + "AnswerRecordsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflow_v2/services/answer_records/transports/base.py b/google/cloud/dialogflow_v2/services/answer_records/transports/base.py new file mode 100644 index 000000000..01e16c3e5 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/answer_records/transports/base.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. +# + +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.dialogflow_v2.types import answer_record +from google.cloud.dialogflow_v2.types import answer_record as gcd_answer_record + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class AnswerRecordsTransport(abc.ABC): + """Abstract transport class for AnswerRecords.""" + + 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_answer_records: gapic_v1.method.wrap_method( + self.list_answer_records, default_timeout=None, client_info=client_info, + ), + self.update_answer_record: gapic_v1.method.wrap_method( + self.update_answer_record, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def list_answer_records( + self, + ) -> typing.Callable[ + [answer_record.ListAnswerRecordsRequest], + typing.Union[ + answer_record.ListAnswerRecordsResponse, + typing.Awaitable[answer_record.ListAnswerRecordsResponse], + ], + ]: + raise NotImplementedError() + + @property + def update_answer_record( + self, + ) -> typing.Callable[ + [gcd_answer_record.UpdateAnswerRecordRequest], + typing.Union[ + gcd_answer_record.AnswerRecord, + typing.Awaitable[gcd_answer_record.AnswerRecord], + ], + ]: + raise NotImplementedError() + + +__all__ = ("AnswerRecordsTransport",) diff --git a/google/cloud/dialogflow_v2/services/answer_records/transports/grpc.py b/google/cloud/dialogflow_v2/services/answer_records/transports/grpc.py new file mode 100644 index 000000000..f8c4c1a0c --- /dev/null +++ b/google/cloud/dialogflow_v2/services/answer_records/transports/grpc.py @@ -0,0 +1,312 @@ +# -*- 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.dialogflow_v2.types import answer_record +from google.cloud.dialogflow_v2.types import answer_record as gcd_answer_record + +from .base import AnswerRecordsTransport, DEFAULT_CLIENT_INFO + + +class AnswerRecordsGrpcTransport(AnswerRecordsTransport): + """gRPC backend transport for AnswerRecords. + + Service for managing + [AnswerRecords][google.cloud.dialogflow.v2.AnswerRecord]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + self._stubs = {} # type: Dict[str, Callable] + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + @classmethod + def create_channel( + cls, + host: str = "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 (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def list_answer_records( + self, + ) -> Callable[ + [answer_record.ListAnswerRecordsRequest], + answer_record.ListAnswerRecordsResponse, + ]: + r"""Return a callable for the list answer records method over gRPC. + + Returns the list of all answer records in the + specified project in reverse chronological order. + + Returns: + Callable[[~.ListAnswerRecordsRequest], + ~.ListAnswerRecordsResponse]: + 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_answer_records" not in self._stubs: + self._stubs["list_answer_records"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.AnswerRecords/ListAnswerRecords", + request_serializer=answer_record.ListAnswerRecordsRequest.serialize, + response_deserializer=answer_record.ListAnswerRecordsResponse.deserialize, + ) + return self._stubs["list_answer_records"] + + @property + def update_answer_record( + self, + ) -> Callable[ + [gcd_answer_record.UpdateAnswerRecordRequest], gcd_answer_record.AnswerRecord + ]: + r"""Return a callable for the update answer record method over gRPC. + + Updates the specified answer record. + + Returns: + Callable[[~.UpdateAnswerRecordRequest], + ~.AnswerRecord]: + 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_answer_record" not in self._stubs: + self._stubs["update_answer_record"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.AnswerRecords/UpdateAnswerRecord", + request_serializer=gcd_answer_record.UpdateAnswerRecordRequest.serialize, + response_deserializer=gcd_answer_record.AnswerRecord.deserialize, + ) + return self._stubs["update_answer_record"] + + +__all__ = ("AnswerRecordsGrpcTransport",) diff --git a/google/cloud/dialogflow_v2/services/answer_records/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/answer_records/transports/grpc_asyncio.py new file mode 100644 index 000000000..e71fd9450 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/answer_records/transports/grpc_asyncio.py @@ -0,0 +1,317 @@ +# -*- 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.dialogflow_v2.types import answer_record +from google.cloud.dialogflow_v2.types import answer_record as gcd_answer_record + +from .base import AnswerRecordsTransport, DEFAULT_CLIENT_INFO +from .grpc import AnswerRecordsGrpcTransport + + +class AnswerRecordsGrpcAsyncIOTransport(AnswerRecordsTransport): + """gRPC AsyncIO backend transport for AnswerRecords. + + Service for managing + [AnswerRecords][google.cloud.dialogflow.v2.AnswerRecord]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + self._stubs = {} + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def list_answer_records( + self, + ) -> Callable[ + [answer_record.ListAnswerRecordsRequest], + Awaitable[answer_record.ListAnswerRecordsResponse], + ]: + r"""Return a callable for the list answer records method over gRPC. + + Returns the list of all answer records in the + specified project in reverse chronological order. + + Returns: + Callable[[~.ListAnswerRecordsRequest], + Awaitable[~.ListAnswerRecordsResponse]]: + 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_answer_records" not in self._stubs: + self._stubs["list_answer_records"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.AnswerRecords/ListAnswerRecords", + request_serializer=answer_record.ListAnswerRecordsRequest.serialize, + response_deserializer=answer_record.ListAnswerRecordsResponse.deserialize, + ) + return self._stubs["list_answer_records"] + + @property + def update_answer_record( + self, + ) -> Callable[ + [gcd_answer_record.UpdateAnswerRecordRequest], + Awaitable[gcd_answer_record.AnswerRecord], + ]: + r"""Return a callable for the update answer record method over gRPC. + + Updates the specified answer record. + + Returns: + Callable[[~.UpdateAnswerRecordRequest], + Awaitable[~.AnswerRecord]]: + 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_answer_record" not in self._stubs: + self._stubs["update_answer_record"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.AnswerRecords/UpdateAnswerRecord", + request_serializer=gcd_answer_record.UpdateAnswerRecordRequest.serialize, + response_deserializer=gcd_answer_record.AnswerRecord.deserialize, + ) + return self._stubs["update_answer_record"] + + +__all__ = ("AnswerRecordsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflow_v2/services/contexts/async_client.py b/google/cloud/dialogflow_v2/services/contexts/async_client.py index decc44459..9c318ef5e 100644 --- a/google/cloud/dialogflow_v2/services/contexts/async_client.py +++ b/google/cloud/dialogflow_v2/services/contexts/async_client.py @@ -71,7 +71,36 @@ class ContextsAsyncClient: common_location_path = staticmethod(ContextsClient.common_location_path) parse_common_location_path = staticmethod(ContextsClient.parse_common_location_path) - from_service_account_file = ContextsClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ContextsAsyncClient: The constructed client. + """ + return ContextsClient.from_service_account_info.__func__(ContextsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ContextsAsyncClient: The constructed client. + """ + return ContextsClient.from_service_account_file.__func__(ContextsAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -148,7 +177,7 @@ async def list_contexts( session. Args: - request (:class:`~.context.ListContextsRequest`): + request (:class:`google.cloud.dialogflow_v2.types.ListContextsRequest`): The request object. The request message for [Contexts.ListContexts][google.cloud.dialogflow.v2.Contexts.ListContexts]. parent (:class:`str`): @@ -158,6 +187,7 @@ async def list_contexts( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -169,7 +199,7 @@ async def list_contexts( sent along with the request as metadata. Returns: - ~.pagers.ListContextsAsyncPager: + google.cloud.dialogflow_v2.services.contexts.pagers.ListContextsAsyncPager: The response message for [Contexts.ListContexts][google.cloud.dialogflow.v2.Contexts.ListContexts]. @@ -233,7 +263,7 @@ async def get_context( r"""Retrieves the specified context. Args: - request (:class:`~.context.GetContextRequest`): + request (:class:`google.cloud.dialogflow_v2.types.GetContextRequest`): The request object. The request message for [Contexts.GetContext][google.cloud.dialogflow.v2.Contexts.GetContext]. name (:class:`str`): @@ -244,6 +274,7 @@ async def get_context( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -255,26 +286,26 @@ async def get_context( sent along with the request as metadata. Returns: - ~.context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -330,7 +361,7 @@ async def create_context( context. Args: - request (:class:`~.gcd_context.CreateContextRequest`): + request (:class:`google.cloud.dialogflow_v2.types.CreateContextRequest`): The request object. The request message for [Contexts.CreateContext][google.cloud.dialogflow.v2.Contexts.CreateContext]. parent (:class:`str`): @@ -340,10 +371,11 @@ async def create_context( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - context (:class:`~.gcd_context.Context`): + context (:class:`google.cloud.dialogflow_v2.types.Context`): Required. The context to create. This corresponds to the ``context`` field on the ``request`` instance; if ``request`` is provided, this @@ -356,26 +388,26 @@ async def create_context( sent along with the request as metadata. Returns: - ~.gcd_context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -431,17 +463,18 @@ async def update_context( r"""Updates the specified context. Args: - request (:class:`~.gcd_context.UpdateContextRequest`): + request (:class:`google.cloud.dialogflow_v2.types.UpdateContextRequest`): The request object. The request message for [Contexts.UpdateContext][google.cloud.dialogflow.v2.Contexts.UpdateContext]. - context (:class:`~.gcd_context.Context`): + context (:class:`google.cloud.dialogflow_v2.types.Context`): Required. The context to update. This corresponds to the ``context`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): Optional. 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. @@ -453,26 +486,26 @@ async def update_context( sent along with the request as metadata. Returns: - ~.gcd_context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -529,7 +562,7 @@ async def delete_context( r"""Deletes the specified context. Args: - request (:class:`~.context.DeleteContextRequest`): + request (:class:`google.cloud.dialogflow_v2.types.DeleteContextRequest`): The request object. The request message for [Contexts.DeleteContext][google.cloud.dialogflow.v2.Contexts.DeleteContext]. name (:class:`str`): @@ -540,6 +573,7 @@ async def delete_context( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -599,7 +633,7 @@ async def delete_all_contexts( r"""Deletes all active contexts in the specified session. Args: - request (:class:`~.context.DeleteAllContextsRequest`): + request (:class:`google.cloud.dialogflow_v2.types.DeleteAllContextsRequest`): The request object. The request message for [Contexts.DeleteAllContexts][google.cloud.dialogflow.v2.Contexts.DeleteAllContexts]. parent (:class:`str`): @@ -610,6 +644,7 @@ async def delete_all_contexts( If ``Environment ID`` is not specified we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. diff --git a/google/cloud/dialogflow_v2/services/contexts/client.py b/google/cloud/dialogflow_v2/services/contexts/client.py index 6bd1fe458..6e32eab42 100644 --- a/google/cloud/dialogflow_v2/services/contexts/client.py +++ b/google/cloud/dialogflow_v2/services/contexts/client.py @@ -111,6 +111,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ContextsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -123,7 +139,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + ContextsClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -231,10 +247,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.ContextsTransport]): The + transport (Union[str, ContextsTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (client_options_lib.ClientOptions): Custom options for the + client_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -270,21 +286,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -327,7 +339,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -345,16 +357,17 @@ def list_contexts( session. Args: - request (:class:`~.context.ListContextsRequest`): + request (google.cloud.dialogflow_v2.types.ListContextsRequest): The request object. The request message for [Contexts.ListContexts][google.cloud.dialogflow.v2.Contexts.ListContexts]. - parent (:class:`str`): + parent (str): Required. The session to list all contexts from. Format: ``projects//agent/sessions/`` or ``projects//agent/environments//users//sessions/``. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -366,7 +379,7 @@ def list_contexts( sent along with the request as metadata. Returns: - ~.pagers.ListContextsPager: + google.cloud.dialogflow_v2.services.contexts.pagers.ListContextsPager: The response message for [Contexts.ListContexts][google.cloud.dialogflow.v2.Contexts.ListContexts]. @@ -431,10 +444,10 @@ def get_context( r"""Retrieves the specified context. Args: - request (:class:`~.context.GetContextRequest`): + request (google.cloud.dialogflow_v2.types.GetContextRequest): The request object. The request message for [Contexts.GetContext][google.cloud.dialogflow.v2.Contexts.GetContext]. - name (:class:`str`): + name (str): Required. The name of the context. Format: ``projects//agent/sessions//contexts/`` or @@ -442,6 +455,7 @@ def get_context( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -453,26 +467,26 @@ def get_context( sent along with the request as metadata. Returns: - ~.context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -529,20 +543,21 @@ def create_context( context. Args: - request (:class:`~.gcd_context.CreateContextRequest`): + request (google.cloud.dialogflow_v2.types.CreateContextRequest): The request object. The request message for [Contexts.CreateContext][google.cloud.dialogflow.v2.Contexts.CreateContext]. - parent (:class:`str`): + parent (str): Required. The session to create a context for. Format: ``projects//agent/sessions/`` or ``projects//agent/environments//users//sessions/``. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - context (:class:`~.gcd_context.Context`): + context (google.cloud.dialogflow_v2.types.Context): Required. The context to create. This corresponds to the ``context`` field on the ``request`` instance; if ``request`` is provided, this @@ -555,26 +570,26 @@ def create_context( sent along with the request as metadata. Returns: - ~.gcd_context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -631,17 +646,18 @@ def update_context( r"""Updates the specified context. Args: - request (:class:`~.gcd_context.UpdateContextRequest`): + request (google.cloud.dialogflow_v2.types.UpdateContextRequest): The request object. The request message for [Contexts.UpdateContext][google.cloud.dialogflow.v2.Contexts.UpdateContext]. - context (:class:`~.gcd_context.Context`): + context (google.cloud.dialogflow_v2.types.Context): Required. The context to update. This corresponds to the ``context`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. 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. @@ -653,26 +669,26 @@ def update_context( sent along with the request as metadata. Returns: - ~.gcd_context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -730,10 +746,10 @@ def delete_context( r"""Deletes the specified context. Args: - request (:class:`~.context.DeleteContextRequest`): + request (google.cloud.dialogflow_v2.types.DeleteContextRequest): The request object. The request message for [Contexts.DeleteContext][google.cloud.dialogflow.v2.Contexts.DeleteContext]. - name (:class:`str`): + name (str): Required. The name of the context to delete. Format: ``projects//agent/sessions//contexts/`` or @@ -741,6 +757,7 @@ def delete_context( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -801,10 +818,10 @@ def delete_all_contexts( r"""Deletes all active contexts in the specified session. Args: - request (:class:`~.context.DeleteAllContextsRequest`): + request (google.cloud.dialogflow_v2.types.DeleteAllContextsRequest): The request object. The request message for [Contexts.DeleteAllContexts][google.cloud.dialogflow.v2.Contexts.DeleteAllContexts]. - parent (:class:`str`): + parent (str): Required. The name of the session to delete all contexts from. Format: ``projects//agent/sessions/`` or @@ -812,6 +829,7 @@ def delete_all_contexts( If ``Environment ID`` is not specified we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. diff --git a/google/cloud/dialogflow_v2/services/contexts/pagers.py b/google/cloud/dialogflow_v2/services/contexts/pagers.py index 93b4e65e1..90adad946 100644 --- a/google/cloud/dialogflow_v2/services/contexts/pagers.py +++ b/google/cloud/dialogflow_v2/services/contexts/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2.types import context @@ -24,7 +33,7 @@ class ListContextsPager: """A pager for iterating through ``list_contexts`` requests. This class thinly wraps an initial - :class:`~.context.ListContextsResponse` object, and + :class:`google.cloud.dialogflow_v2.types.ListContextsResponse` object, and provides an ``__iter__`` method to iterate through its ``contexts`` field. @@ -33,7 +42,7 @@ class ListContextsPager: through the ``contexts`` field on the corresponding responses. - All the usual :class:`~.context.ListContextsResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.ListContextsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.context.ListContextsRequest`): + request (google.cloud.dialogflow_v2.types.ListContextsRequest): The initial request object. - response (:class:`~.context.ListContextsResponse`): + response (google.cloud.dialogflow_v2.types.ListContextsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListContextsAsyncPager: """A pager for iterating through ``list_contexts`` requests. This class thinly wraps an initial - :class:`~.context.ListContextsResponse` object, and + :class:`google.cloud.dialogflow_v2.types.ListContextsResponse` object, and provides an ``__aiter__`` method to iterate through its ``contexts`` field. @@ -95,7 +104,7 @@ class ListContextsAsyncPager: through the ``contexts`` field on the corresponding responses. - All the usual :class:`~.context.ListContextsResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.ListContextsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.context.ListContextsRequest`): + request (google.cloud.dialogflow_v2.types.ListContextsRequest): The initial request object. - response (:class:`~.context.ListContextsResponse`): + response (google.cloud.dialogflow_v2.types.ListContextsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2/services/contexts/transports/grpc.py b/google/cloud/dialogflow_v2/services/contexts/transports/grpc.py index dee8c8841..dfeab86c6 100644 --- a/google/cloud/dialogflow_v2/services/contexts/transports/grpc.py +++ b/google/cloud/dialogflow_v2/services/contexts/transports/grpc.py @@ -59,6 +59,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -89,6 +90,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -105,6 +110,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -114,11 +124,6 @@ def __init__( 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 @@ -162,12 +167,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/contexts/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/contexts/transports/grpc_asyncio.py index d112df150..680211350 100644 --- a/google/cloud/dialogflow_v2/services/contexts/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2/services/contexts/transports/grpc_asyncio.py @@ -103,6 +103,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -134,6 +135,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -150,6 +155,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -159,11 +169,6 @@ def __init__( 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 @@ -207,12 +212,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/conversation_profiles/__init__.py b/google/cloud/dialogflow_v2/services/conversation_profiles/__init__.py new file mode 100644 index 000000000..9ab8de0b8 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversation_profiles/__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 ConversationProfilesClient +from .async_client import ConversationProfilesAsyncClient + +__all__ = ( + "ConversationProfilesClient", + "ConversationProfilesAsyncClient", +) diff --git a/google/cloud/dialogflow_v2/services/conversation_profiles/async_client.py b/google/cloud/dialogflow_v2/services/conversation_profiles/async_client.py new file mode 100644 index 000000000..e66165698 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversation_profiles/async_client.py @@ -0,0 +1,612 @@ +# -*- 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.dialogflow_v2.services.conversation_profiles import pagers +from google.cloud.dialogflow_v2.types import audio_config +from google.cloud.dialogflow_v2.types import conversation_profile +from google.cloud.dialogflow_v2.types import ( + conversation_profile as gcd_conversation_profile, +) +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import ConversationProfilesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import ConversationProfilesGrpcAsyncIOTransport +from .client import ConversationProfilesClient + + +class ConversationProfilesAsyncClient: + """Service for managing + [ConversationProfiles][google.cloud.dialogflow.v2.ConversationProfile]. + """ + + _client: ConversationProfilesClient + + DEFAULT_ENDPOINT = ConversationProfilesClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = ConversationProfilesClient.DEFAULT_MTLS_ENDPOINT + + agent_path = staticmethod(ConversationProfilesClient.agent_path) + parse_agent_path = staticmethod(ConversationProfilesClient.parse_agent_path) + conversation_profile_path = staticmethod( + ConversationProfilesClient.conversation_profile_path + ) + parse_conversation_profile_path = staticmethod( + ConversationProfilesClient.parse_conversation_profile_path + ) + document_path = staticmethod(ConversationProfilesClient.document_path) + parse_document_path = staticmethod(ConversationProfilesClient.parse_document_path) + knowledge_base_path = staticmethod(ConversationProfilesClient.knowledge_base_path) + parse_knowledge_base_path = staticmethod( + ConversationProfilesClient.parse_knowledge_base_path + ) + + common_billing_account_path = staticmethod( + ConversationProfilesClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + ConversationProfilesClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(ConversationProfilesClient.common_folder_path) + parse_common_folder_path = staticmethod( + ConversationProfilesClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + ConversationProfilesClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + ConversationProfilesClient.parse_common_organization_path + ) + + common_project_path = staticmethod(ConversationProfilesClient.common_project_path) + parse_common_project_path = staticmethod( + ConversationProfilesClient.parse_common_project_path + ) + + common_location_path = staticmethod(ConversationProfilesClient.common_location_path) + parse_common_location_path = staticmethod( + ConversationProfilesClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationProfilesAsyncClient: The constructed client. + """ + return ConversationProfilesClient.from_service_account_info.__func__(ConversationProfilesAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationProfilesAsyncClient: The constructed client. + """ + return ConversationProfilesClient.from_service_account_file.__func__(ConversationProfilesAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> ConversationProfilesTransport: + """Return the transport used by the client instance. + + Returns: + ConversationProfilesTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(ConversationProfilesClient).get_transport_class, + type(ConversationProfilesClient), + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, ConversationProfilesTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the conversation profiles 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, ~.ConversationProfilesTransport]): 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 = ConversationProfilesClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_conversation_profiles( + self, + request: conversation_profile.ListConversationProfilesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListConversationProfilesAsyncPager: + r"""Returns the list of all conversation profiles in the + specified project. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.ListConversationProfilesRequest`): + The request object. The request message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2.ConversationProfiles.ListConversationProfiles]. + parent (:class:`str`): + Required. The project to list all conversation profiles + from. 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: + google.cloud.dialogflow_v2.services.conversation_profiles.pagers.ListConversationProfilesAsyncPager: + The response message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2.ConversationProfiles.ListConversationProfiles]. + + 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 = conversation_profile.ListConversationProfilesRequest(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_conversation_profiles, + 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.ListConversationProfilesAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_conversation_profile( + self, + request: conversation_profile.GetConversationProfileRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation_profile.ConversationProfile: + r"""Retrieves the specified conversation profile. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.GetConversationProfileRequest`): + The request object. The request message for + [ConversationProfiles.GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile]. + name (:class:`str`): + Required. The resource name of the conversation profile. + Format: + ``projects//locations//conversationProfiles/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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 = conversation_profile.GetConversationProfileRequest(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_conversation_profile, + 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_conversation_profile( + self, + request: gcd_conversation_profile.CreateConversationProfileRequest = None, + *, + parent: str = None, + conversation_profile: gcd_conversation_profile.ConversationProfile = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation_profile.ConversationProfile: + r"""Creates a conversation profile in the specified project. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile] + API. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.CreateConversationProfileRequest`): + The request object. The request message for + [ConversationProfiles.CreateConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.CreateConversationProfile]. + parent (:class:`str`): + Required. The project to create a conversation profile + for. Format: + ``projects//locations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + conversation_profile (:class:`google.cloud.dialogflow_v2.types.ConversationProfile`): + Required. The conversation profile to + create. + + This corresponds to the ``conversation_profile`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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, conversation_profile]) + if request is 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 = gcd_conversation_profile.CreateConversationProfileRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if conversation_profile is not None: + request.conversation_profile = conversation_profile + + # 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_conversation_profile, + 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_conversation_profile( + self, + request: gcd_conversation_profile.UpdateConversationProfileRequest = None, + *, + conversation_profile: gcd_conversation_profile.ConversationProfile = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation_profile.ConversationProfile: + r"""Updates the specified conversation profile. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile] + API. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.UpdateConversationProfileRequest`): + The request object. The request message for + [ConversationProfiles.UpdateConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.UpdateConversationProfile]. + conversation_profile (:class:`google.cloud.dialogflow_v2.types.ConversationProfile`): + Required. The conversation profile to + update. + + This corresponds to the ``conversation_profile`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): + Required. The mask to control which + fields to update. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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([conversation_profile, 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 = gcd_conversation_profile.UpdateConversationProfileRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if conversation_profile is not None: + request.conversation_profile = conversation_profile + 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_conversation_profile, + 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( + (("conversation_profile.name", request.conversation_profile.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_conversation_profile( + self, + request: conversation_profile.DeleteConversationProfileRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified conversation profile. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.DeleteConversationProfileRequest`): + The request object. The request message for + [ConversationProfiles.DeleteConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.DeleteConversationProfile]. + This operation fails if the conversation profile is + still referenced from a phone number. + name (:class:`str`): + Required. The name of the conversation profile to + delete. Format: + ``projects//locations//conversationProfiles/``. + + 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 = conversation_profile.DeleteConversationProfileRequest(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_conversation_profile, + 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ConversationProfilesAsyncClient",) diff --git a/google/cloud/dialogflow_v2/services/conversation_profiles/client.py b/google/cloud/dialogflow_v2/services/conversation_profiles/client.py new file mode 100644 index 000000000..4f0495e3e --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversation_profiles/client.py @@ -0,0 +1,838 @@ +# -*- 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.dialogflow_v2.services.conversation_profiles import pagers +from google.cloud.dialogflow_v2.types import audio_config +from google.cloud.dialogflow_v2.types import conversation_profile +from google.cloud.dialogflow_v2.types import ( + conversation_profile as gcd_conversation_profile, +) +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import ConversationProfilesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import ConversationProfilesGrpcTransport +from .transports.grpc_asyncio import ConversationProfilesGrpcAsyncIOTransport + + +class ConversationProfilesClientMeta(type): + """Metaclass for the ConversationProfiles 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[ConversationProfilesTransport]] + _transport_registry["grpc"] = ConversationProfilesGrpcTransport + _transport_registry["grpc_asyncio"] = ConversationProfilesGrpcAsyncIOTransport + + def get_transport_class( + cls, label: str = None, + ) -> Type[ConversationProfilesTransport]: + """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 ConversationProfilesClient(metaclass=ConversationProfilesClientMeta): + """Service for managing + [ConversationProfiles][google.cloud.dialogflow.v2.ConversationProfile]. + """ + + @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_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationProfilesClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationProfilesClient: 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) -> ConversationProfilesTransport: + """Return the transport used by the client instance. + + Returns: + ConversationProfilesTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def agent_path(project: str,) -> str: + """Return a fully-qualified agent string.""" + return "projects/{project}/agent".format(project=project,) + + @staticmethod + def parse_agent_path(path: str) -> Dict[str, str]: + """Parse a agent path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/agent$", path) + return m.groupdict() if m else {} + + @staticmethod + def conversation_profile_path(project: str, conversation_profile: str,) -> str: + """Return a fully-qualified conversation_profile string.""" + return "projects/{project}/conversationProfiles/{conversation_profile}".format( + project=project, conversation_profile=conversation_profile, + ) + + @staticmethod + def parse_conversation_profile_path(path: str) -> Dict[str, str]: + """Parse a conversation_profile path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversationProfiles/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def document_path(project: str, knowledge_base: str, document: str,) -> str: + """Return a fully-qualified document string.""" + return "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, knowledge_base=knowledge_base, document=document, + ) + + @staticmethod + def parse_document_path(path: str) -> Dict[str, str]: + """Parse a document path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/knowledgeBases/(?P.+?)/documents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def knowledge_base_path(project: str, knowledge_base: str,) -> str: + """Return a fully-qualified knowledge_base string.""" + return "projects/{project}/knowledgeBases/{knowledge_base}".format( + project=project, knowledge_base=knowledge_base, + ) + + @staticmethod + def parse_knowledge_base_path(path: str) -> Dict[str, str]: + """Parse a knowledge_base path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/knowledgeBases/(?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, ConversationProfilesTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the conversation profiles 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, ConversationProfilesTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, ConversationProfilesTransport): + # transport is a ConversationProfilesTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_conversation_profiles( + self, + request: conversation_profile.ListConversationProfilesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListConversationProfilesPager: + r"""Returns the list of all conversation profiles in the + specified project. + + Args: + request (google.cloud.dialogflow_v2.types.ListConversationProfilesRequest): + The request object. The request message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2.ConversationProfiles.ListConversationProfiles]. + parent (str): + Required. The project to list all conversation profiles + from. 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: + google.cloud.dialogflow_v2.services.conversation_profiles.pagers.ListConversationProfilesPager: + The response message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2.ConversationProfiles.ListConversationProfiles]. + + 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 conversation_profile.ListConversationProfilesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, conversation_profile.ListConversationProfilesRequest + ): + request = conversation_profile.ListConversationProfilesRequest(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_conversation_profiles + ] + + # 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.ListConversationProfilesPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_conversation_profile( + self, + request: conversation_profile.GetConversationProfileRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation_profile.ConversationProfile: + r"""Retrieves the specified conversation profile. + + Args: + request (google.cloud.dialogflow_v2.types.GetConversationProfileRequest): + The request object. The request message for + [ConversationProfiles.GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile]. + name (str): + Required. The resource name of the conversation profile. + Format: + ``projects//locations//conversationProfiles/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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 conversation_profile.GetConversationProfileRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation_profile.GetConversationProfileRequest): + request = conversation_profile.GetConversationProfileRequest(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_conversation_profile] + + # 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_conversation_profile( + self, + request: gcd_conversation_profile.CreateConversationProfileRequest = None, + *, + parent: str = None, + conversation_profile: gcd_conversation_profile.ConversationProfile = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation_profile.ConversationProfile: + r"""Creates a conversation profile in the specified project. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile] + API. + + Args: + request (google.cloud.dialogflow_v2.types.CreateConversationProfileRequest): + The request object. The request message for + [ConversationProfiles.CreateConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.CreateConversationProfile]. + parent (str): + Required. The project to create a conversation profile + for. Format: + ``projects//locations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + conversation_profile (google.cloud.dialogflow_v2.types.ConversationProfile): + Required. The conversation profile to + create. + + This corresponds to the ``conversation_profile`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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, conversation_profile]) + if request is 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 gcd_conversation_profile.CreateConversationProfileRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, gcd_conversation_profile.CreateConversationProfileRequest + ): + request = gcd_conversation_profile.CreateConversationProfileRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if conversation_profile is not None: + request.conversation_profile = conversation_profile + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[ + self._transport.create_conversation_profile + ] + + # 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_conversation_profile( + self, + request: gcd_conversation_profile.UpdateConversationProfileRequest = None, + *, + conversation_profile: gcd_conversation_profile.ConversationProfile = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation_profile.ConversationProfile: + r"""Updates the specified conversation profile. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile] + API. + + Args: + request (google.cloud.dialogflow_v2.types.UpdateConversationProfileRequest): + The request object. The request message for + [ConversationProfiles.UpdateConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.UpdateConversationProfile]. + conversation_profile (google.cloud.dialogflow_v2.types.ConversationProfile): + Required. The conversation profile to + update. + + This corresponds to the ``conversation_profile`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. The mask to control which + fields to update. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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([conversation_profile, 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 gcd_conversation_profile.UpdateConversationProfileRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, gcd_conversation_profile.UpdateConversationProfileRequest + ): + request = gcd_conversation_profile.UpdateConversationProfileRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if conversation_profile is not None: + request.conversation_profile = conversation_profile + 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_conversation_profile + ] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("conversation_profile.name", request.conversation_profile.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def delete_conversation_profile( + self, + request: conversation_profile.DeleteConversationProfileRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified conversation profile. + + Args: + request (google.cloud.dialogflow_v2.types.DeleteConversationProfileRequest): + The request object. The request message for + [ConversationProfiles.DeleteConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.DeleteConversationProfile]. + This operation fails if the conversation profile is + still referenced from a phone number. + name (str): + Required. The name of the conversation profile to + delete. Format: + ``projects//locations//conversationProfiles/``. + + 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 conversation_profile.DeleteConversationProfileRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, conversation_profile.DeleteConversationProfileRequest + ): + request = conversation_profile.DeleteConversationProfileRequest(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_conversation_profile + ] + + # 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ConversationProfilesClient",) diff --git a/google/cloud/dialogflow_v2/services/conversation_profiles/pagers.py b/google/cloud/dialogflow_v2/services/conversation_profiles/pagers.py new file mode 100644 index 000000000..0e261b842 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversation_profiles/pagers.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.dialogflow_v2.types import conversation_profile + + +class ListConversationProfilesPager: + """A pager for iterating through ``list_conversation_profiles`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListConversationProfilesResponse` object, and + provides an ``__iter__`` method to iterate through its + ``conversation_profiles`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListConversationProfiles`` requests and continue to iterate + through the ``conversation_profiles`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListConversationProfilesResponse` + 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[..., conversation_profile.ListConversationProfilesResponse], + request: conversation_profile.ListConversationProfilesRequest, + response: conversation_profile.ListConversationProfilesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListConversationProfilesRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListConversationProfilesResponse): + 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 = conversation_profile.ListConversationProfilesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[conversation_profile.ListConversationProfilesResponse]: + 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[conversation_profile.ConversationProfile]: + for page in self.pages: + yield from page.conversation_profiles + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListConversationProfilesAsyncPager: + """A pager for iterating through ``list_conversation_profiles`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListConversationProfilesResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``conversation_profiles`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListConversationProfiles`` requests and continue to iterate + through the ``conversation_profiles`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListConversationProfilesResponse` + 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[conversation_profile.ListConversationProfilesResponse] + ], + request: conversation_profile.ListConversationProfilesRequest, + response: conversation_profile.ListConversationProfilesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListConversationProfilesRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListConversationProfilesResponse): + 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 = conversation_profile.ListConversationProfilesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages( + self, + ) -> AsyncIterable[conversation_profile.ListConversationProfilesResponse]: + 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[conversation_profile.ConversationProfile]: + async def async_generator(): + async for page in self.pages: + for response in page.conversation_profiles: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflow_v2/services/conversation_profiles/transports/__init__.py b/google/cloud/dialogflow_v2/services/conversation_profiles/transports/__init__.py new file mode 100644 index 000000000..e97774bcf --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversation_profiles/transports/__init__.py @@ -0,0 +1,37 @@ +# -*- 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 ConversationProfilesTransport +from .grpc import ConversationProfilesGrpcTransport +from .grpc_asyncio import ConversationProfilesGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = ( + OrderedDict() +) # type: Dict[str, Type[ConversationProfilesTransport]] +_transport_registry["grpc"] = ConversationProfilesGrpcTransport +_transport_registry["grpc_asyncio"] = ConversationProfilesGrpcAsyncIOTransport + +__all__ = ( + "ConversationProfilesTransport", + "ConversationProfilesGrpcTransport", + "ConversationProfilesGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflow_v2/services/conversation_profiles/transports/base.py b/google/cloud/dialogflow_v2/services/conversation_profiles/transports/base.py new file mode 100644 index 000000000..fad34b214 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversation_profiles/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.dialogflow_v2.types import conversation_profile +from google.cloud.dialogflow_v2.types import ( + conversation_profile as gcd_conversation_profile, +) +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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class ConversationProfilesTransport(abc.ABC): + """Abstract transport class for ConversationProfiles.""" + + 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_conversation_profiles: gapic_v1.method.wrap_method( + self.list_conversation_profiles, + default_timeout=None, + client_info=client_info, + ), + self.get_conversation_profile: gapic_v1.method.wrap_method( + self.get_conversation_profile, + default_timeout=None, + client_info=client_info, + ), + self.create_conversation_profile: gapic_v1.method.wrap_method( + self.create_conversation_profile, + default_timeout=None, + client_info=client_info, + ), + self.update_conversation_profile: gapic_v1.method.wrap_method( + self.update_conversation_profile, + default_timeout=None, + client_info=client_info, + ), + self.delete_conversation_profile: gapic_v1.method.wrap_method( + self.delete_conversation_profile, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def list_conversation_profiles( + self, + ) -> typing.Callable[ + [conversation_profile.ListConversationProfilesRequest], + typing.Union[ + conversation_profile.ListConversationProfilesResponse, + typing.Awaitable[conversation_profile.ListConversationProfilesResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_conversation_profile( + self, + ) -> typing.Callable[ + [conversation_profile.GetConversationProfileRequest], + typing.Union[ + conversation_profile.ConversationProfile, + typing.Awaitable[conversation_profile.ConversationProfile], + ], + ]: + raise NotImplementedError() + + @property + def create_conversation_profile( + self, + ) -> typing.Callable[ + [gcd_conversation_profile.CreateConversationProfileRequest], + typing.Union[ + gcd_conversation_profile.ConversationProfile, + typing.Awaitable[gcd_conversation_profile.ConversationProfile], + ], + ]: + raise NotImplementedError() + + @property + def update_conversation_profile( + self, + ) -> typing.Callable[ + [gcd_conversation_profile.UpdateConversationProfileRequest], + typing.Union[ + gcd_conversation_profile.ConversationProfile, + typing.Awaitable[gcd_conversation_profile.ConversationProfile], + ], + ]: + raise NotImplementedError() + + @property + def delete_conversation_profile( + self, + ) -> typing.Callable[ + [conversation_profile.DeleteConversationProfileRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + +__all__ = ("ConversationProfilesTransport",) diff --git a/google/cloud/dialogflow_v2/services/conversation_profiles/transports/grpc.py b/google/cloud/dialogflow_v2/services/conversation_profiles/transports/grpc.py new file mode 100644 index 000000000..41f695f7e --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversation_profiles/transports/grpc.py @@ -0,0 +1,412 @@ +# -*- 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.dialogflow_v2.types import conversation_profile +from google.cloud.dialogflow_v2.types import ( + conversation_profile as gcd_conversation_profile, +) +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import ConversationProfilesTransport, DEFAULT_CLIENT_INFO + + +class ConversationProfilesGrpcTransport(ConversationProfilesTransport): + """gRPC backend transport for ConversationProfiles. + + Service for managing + [ConversationProfiles][google.cloud.dialogflow.v2.ConversationProfile]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + self._stubs = {} # type: Dict[str, Callable] + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + @classmethod + def create_channel( + cls, + host: str = "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 (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def list_conversation_profiles( + self, + ) -> Callable[ + [conversation_profile.ListConversationProfilesRequest], + conversation_profile.ListConversationProfilesResponse, + ]: + r"""Return a callable for the list conversation profiles method over gRPC. + + Returns the list of all conversation profiles in the + specified project. + + Returns: + Callable[[~.ListConversationProfilesRequest], + ~.ListConversationProfilesResponse]: + 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_conversation_profiles" not in self._stubs: + self._stubs["list_conversation_profiles"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.ConversationProfiles/ListConversationProfiles", + request_serializer=conversation_profile.ListConversationProfilesRequest.serialize, + response_deserializer=conversation_profile.ListConversationProfilesResponse.deserialize, + ) + return self._stubs["list_conversation_profiles"] + + @property + def get_conversation_profile( + self, + ) -> Callable[ + [conversation_profile.GetConversationProfileRequest], + conversation_profile.ConversationProfile, + ]: + r"""Return a callable for the get conversation profile method over gRPC. + + Retrieves the specified conversation profile. + + Returns: + Callable[[~.GetConversationProfileRequest], + ~.ConversationProfile]: + 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_conversation_profile" not in self._stubs: + self._stubs["get_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.ConversationProfiles/GetConversationProfile", + request_serializer=conversation_profile.GetConversationProfileRequest.serialize, + response_deserializer=conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["get_conversation_profile"] + + @property + def create_conversation_profile( + self, + ) -> Callable[ + [gcd_conversation_profile.CreateConversationProfileRequest], + gcd_conversation_profile.ConversationProfile, + ]: + r"""Return a callable for the create conversation profile method over gRPC. + + Creates a conversation profile in the specified project. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile] + API. + + Returns: + Callable[[~.CreateConversationProfileRequest], + ~.ConversationProfile]: + 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_conversation_profile" not in self._stubs: + self._stubs["create_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.ConversationProfiles/CreateConversationProfile", + request_serializer=gcd_conversation_profile.CreateConversationProfileRequest.serialize, + response_deserializer=gcd_conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["create_conversation_profile"] + + @property + def update_conversation_profile( + self, + ) -> Callable[ + [gcd_conversation_profile.UpdateConversationProfileRequest], + gcd_conversation_profile.ConversationProfile, + ]: + r"""Return a callable for the update conversation profile method over gRPC. + + Updates the specified conversation profile. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile] + API. + + Returns: + Callable[[~.UpdateConversationProfileRequest], + ~.ConversationProfile]: + 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_conversation_profile" not in self._stubs: + self._stubs["update_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.ConversationProfiles/UpdateConversationProfile", + request_serializer=gcd_conversation_profile.UpdateConversationProfileRequest.serialize, + response_deserializer=gcd_conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["update_conversation_profile"] + + @property + def delete_conversation_profile( + self, + ) -> Callable[[conversation_profile.DeleteConversationProfileRequest], empty.Empty]: + r"""Return a callable for the delete conversation profile method over gRPC. + + Deletes the specified conversation profile. + + Returns: + Callable[[~.DeleteConversationProfileRequest], + ~.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_conversation_profile" not in self._stubs: + self._stubs["delete_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.ConversationProfiles/DeleteConversationProfile", + request_serializer=conversation_profile.DeleteConversationProfileRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_conversation_profile"] + + +__all__ = ("ConversationProfilesGrpcTransport",) diff --git a/google/cloud/dialogflow_v2/services/conversation_profiles/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/conversation_profiles/transports/grpc_asyncio.py new file mode 100644 index 000000000..acb03edac --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversation_profiles/transports/grpc_asyncio.py @@ -0,0 +1,418 @@ +# -*- 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.dialogflow_v2.types import conversation_profile +from google.cloud.dialogflow_v2.types import ( + conversation_profile as gcd_conversation_profile, +) +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import ConversationProfilesTransport, DEFAULT_CLIENT_INFO +from .grpc import ConversationProfilesGrpcTransport + + +class ConversationProfilesGrpcAsyncIOTransport(ConversationProfilesTransport): + """gRPC AsyncIO backend transport for ConversationProfiles. + + Service for managing + [ConversationProfiles][google.cloud.dialogflow.v2.ConversationProfile]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + self._stubs = {} + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def list_conversation_profiles( + self, + ) -> Callable[ + [conversation_profile.ListConversationProfilesRequest], + Awaitable[conversation_profile.ListConversationProfilesResponse], + ]: + r"""Return a callable for the list conversation profiles method over gRPC. + + Returns the list of all conversation profiles in the + specified project. + + Returns: + Callable[[~.ListConversationProfilesRequest], + Awaitable[~.ListConversationProfilesResponse]]: + 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_conversation_profiles" not in self._stubs: + self._stubs["list_conversation_profiles"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.ConversationProfiles/ListConversationProfiles", + request_serializer=conversation_profile.ListConversationProfilesRequest.serialize, + response_deserializer=conversation_profile.ListConversationProfilesResponse.deserialize, + ) + return self._stubs["list_conversation_profiles"] + + @property + def get_conversation_profile( + self, + ) -> Callable[ + [conversation_profile.GetConversationProfileRequest], + Awaitable[conversation_profile.ConversationProfile], + ]: + r"""Return a callable for the get conversation profile method over gRPC. + + Retrieves the specified conversation profile. + + Returns: + Callable[[~.GetConversationProfileRequest], + Awaitable[~.ConversationProfile]]: + 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_conversation_profile" not in self._stubs: + self._stubs["get_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.ConversationProfiles/GetConversationProfile", + request_serializer=conversation_profile.GetConversationProfileRequest.serialize, + response_deserializer=conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["get_conversation_profile"] + + @property + def create_conversation_profile( + self, + ) -> Callable[ + [gcd_conversation_profile.CreateConversationProfileRequest], + Awaitable[gcd_conversation_profile.ConversationProfile], + ]: + r"""Return a callable for the create conversation profile method over gRPC. + + Creates a conversation profile in the specified project. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile] + API. + + Returns: + Callable[[~.CreateConversationProfileRequest], + Awaitable[~.ConversationProfile]]: + 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_conversation_profile" not in self._stubs: + self._stubs["create_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.ConversationProfiles/CreateConversationProfile", + request_serializer=gcd_conversation_profile.CreateConversationProfileRequest.serialize, + response_deserializer=gcd_conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["create_conversation_profile"] + + @property + def update_conversation_profile( + self, + ) -> Callable[ + [gcd_conversation_profile.UpdateConversationProfileRequest], + Awaitable[gcd_conversation_profile.ConversationProfile], + ]: + r"""Return a callable for the update conversation profile method over gRPC. + + Updates the specified conversation profile. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile] + API. + + Returns: + Callable[[~.UpdateConversationProfileRequest], + Awaitable[~.ConversationProfile]]: + 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_conversation_profile" not in self._stubs: + self._stubs["update_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.ConversationProfiles/UpdateConversationProfile", + request_serializer=gcd_conversation_profile.UpdateConversationProfileRequest.serialize, + response_deserializer=gcd_conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["update_conversation_profile"] + + @property + def delete_conversation_profile( + self, + ) -> Callable[ + [conversation_profile.DeleteConversationProfileRequest], Awaitable[empty.Empty] + ]: + r"""Return a callable for the delete conversation profile method over gRPC. + + Deletes the specified conversation profile. + + Returns: + Callable[[~.DeleteConversationProfileRequest], + 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_conversation_profile" not in self._stubs: + self._stubs["delete_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.ConversationProfiles/DeleteConversationProfile", + request_serializer=conversation_profile.DeleteConversationProfileRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_conversation_profile"] + + +__all__ = ("ConversationProfilesGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflow_v2/services/conversations/__init__.py b/google/cloud/dialogflow_v2/services/conversations/__init__.py new file mode 100644 index 000000000..cce809abe --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversations/__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 ConversationsClient +from .async_client import ConversationsAsyncClient + +__all__ = ( + "ConversationsClient", + "ConversationsAsyncClient", +) diff --git a/google/cloud/dialogflow_v2/services/conversations/async_client.py b/google/cloud/dialogflow_v2/services/conversations/async_client.py new file mode 100644 index 000000000..5e1ee33d9 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversations/async_client.py @@ -0,0 +1,901 @@ +# -*- 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.dialogflow_v2.services.conversations import pagers +from google.cloud.dialogflow_v2.types import conversation +from google.cloud.dialogflow_v2.types import conversation as gcd_conversation +from google.cloud.dialogflow_v2.types import participant +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import ConversationsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import ConversationsGrpcAsyncIOTransport +from .client import ConversationsClient + + +class ConversationsAsyncClient: + """Service for managing + [Conversations][google.cloud.dialogflow.v2.Conversation]. + """ + + _client: ConversationsClient + + DEFAULT_ENDPOINT = ConversationsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = ConversationsClient.DEFAULT_MTLS_ENDPOINT + + call_matcher_path = staticmethod(ConversationsClient.call_matcher_path) + parse_call_matcher_path = staticmethod(ConversationsClient.parse_call_matcher_path) + conversation_path = staticmethod(ConversationsClient.conversation_path) + parse_conversation_path = staticmethod(ConversationsClient.parse_conversation_path) + conversation_profile_path = staticmethod( + ConversationsClient.conversation_profile_path + ) + parse_conversation_profile_path = staticmethod( + ConversationsClient.parse_conversation_profile_path + ) + message_path = staticmethod(ConversationsClient.message_path) + parse_message_path = staticmethod(ConversationsClient.parse_message_path) + + common_billing_account_path = staticmethod( + ConversationsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + ConversationsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(ConversationsClient.common_folder_path) + parse_common_folder_path = staticmethod( + ConversationsClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + ConversationsClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + ConversationsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(ConversationsClient.common_project_path) + parse_common_project_path = staticmethod( + ConversationsClient.parse_common_project_path + ) + + common_location_path = staticmethod(ConversationsClient.common_location_path) + parse_common_location_path = staticmethod( + ConversationsClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationsAsyncClient: The constructed client. + """ + return ConversationsClient.from_service_account_info.__func__(ConversationsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationsAsyncClient: The constructed client. + """ + return ConversationsClient.from_service_account_file.__func__(ConversationsAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> ConversationsTransport: + """Return the transport used by the client instance. + + Returns: + ConversationsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(ConversationsClient).get_transport_class, type(ConversationsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, ConversationsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the conversations 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, ~.ConversationsTransport]): 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 = ConversationsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def create_conversation( + self, + request: gcd_conversation.CreateConversationRequest = None, + *, + parent: str = None, + conversation: gcd_conversation.Conversation = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation.Conversation: + r"""Creates a new conversation. Conversations are auto-completed + after 24 hours. + + Conversation Lifecycle: There are two stages during a + conversation: Automated Agent Stage and Assist Stage. + + For Automated Agent Stage, there will be a dialogflow agent + responding to user queries. + + For Assist Stage, there's no dialogflow agent responding to user + queries. But we will provide suggestions which are generated + from conversation. + + If + [Conversation.conversation_profile][google.cloud.dialogflow.v2.Conversation.conversation_profile] + is configured for a dialogflow agent, conversation will start + from ``Automated Agent Stage``, otherwise, it will start from + ``Assist Stage``. And during ``Automated Agent Stage``, once an + [Intent][google.cloud.dialogflow.v2.Intent] with + [Intent.live_agent_handoff][google.cloud.dialogflow.v2.Intent.live_agent_handoff] + is triggered, conversation will transfer to Assist Stage. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.CreateConversationRequest`): + The request object. The request message for + [Conversations.CreateConversation][google.cloud.dialogflow.v2.Conversations.CreateConversation]. + parent (:class:`str`): + Required. Resource identifier of the project creating + the conversation. Format: + ``projects//locations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + conversation (:class:`google.cloud.dialogflow_v2.types.Conversation`): + Required. The conversation to create. + This corresponds to the ``conversation`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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, conversation]) + if request is 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 = gcd_conversation.CreateConversationRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if conversation is not None: + request.conversation = conversation + + # 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_conversation, + 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 list_conversations( + self, + request: conversation.ListConversationsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListConversationsAsyncPager: + r"""Returns the list of all conversations in the + specified project. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.ListConversationsRequest`): + The request object. The request message for + [Conversations.ListConversations][google.cloud.dialogflow.v2.Conversations.ListConversations]. + parent (:class:`str`): + Required. The project from which to list all + conversation. 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: + google.cloud.dialogflow_v2.services.conversations.pagers.ListConversationsAsyncPager: + The response message for + [Conversations.ListConversations][google.cloud.dialogflow.v2.Conversations.ListConversations]. + + 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 = conversation.ListConversationsRequest(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_conversations, + 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.ListConversationsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_conversation( + self, + request: conversation.GetConversationRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.Conversation: + r"""Retrieves the specific conversation. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.GetConversationRequest`): + The request object. The request message for + [Conversations.GetConversation][google.cloud.dialogflow.v2.Conversations.GetConversation]. + name (:class:`str`): + Required. The name of the conversation. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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 = conversation.GetConversationRequest(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_conversation, + 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 complete_conversation( + self, + request: conversation.CompleteConversationRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.Conversation: + r"""Completes the specified conversation. Finished + conversations are purged from the database after 30 + days. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.CompleteConversationRequest`): + The request object. The request message for + [Conversations.CompleteConversation][google.cloud.dialogflow.v2.Conversations.CompleteConversation]. + name (:class:`str`): + Required. Resource identifier of the conversation to + close. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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 = conversation.CompleteConversationRequest(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.complete_conversation, + 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_call_matcher( + self, + request: conversation.CreateCallMatcherRequest = None, + *, + parent: str = None, + call_matcher: conversation.CallMatcher = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.CallMatcher: + r"""Creates a call matcher that links incoming SIP calls + to the specified conversation if they fulfill specified + criteria. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.CreateCallMatcherRequest`): + The request object. The request message for + [Conversations.CreateCallMatcher][google.cloud.dialogflow.v2.Conversations.CreateCallMatcher]. + parent (:class:`str`): + Required. Resource identifier of the conversation adding + the call matcher. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + call_matcher (:class:`google.cloud.dialogflow_v2.types.CallMatcher`): + Required. The call matcher to create. + This corresponds to the ``call_matcher`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.CallMatcher: + Represents a call matcher that describes criteria for matching incoming SIP + calls to a conversation. When Dialogflow get a SIP + call from a third-party carrier, Dialogflow matches + the call to an existing conversation by either: + + - Extracting the conversation id from the [Call-Info + header](\ https://tools.ietf.org/html/rfc3261#section-20.9), + e.g. Call-Info: + + ;purpose=Goog-ContactCenter-Conversation. + + \* Or, if that doesn't work, matching incoming [SIP + headers](\ https://tools.ietf.org/html/rfc3261#section-7.3) + against any + [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] + for the conversation. + + If an incoming SIP call without valid Call-Info + header matches to zero or multiple conversations with + CallMatcher, we reject it. + + A call matcher contains equality conditions for SIP + headers that all have to be fulfilled in order for a + SIP call to match. + + The matched SIP headers consist of well-known headers + (To, From, Call-ID) and custom headers. A + [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] + is only valid if it specifies: + + - At least 1 custom header, + - or at least 2 well-known headers. + + """ + # Create or coerce a 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, call_matcher]) + if request is 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 = conversation.CreateCallMatcherRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if call_matcher is not None: + request.call_matcher = call_matcher + + # 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_call_matcher, + 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 list_call_matchers( + self, + request: conversation.ListCallMatchersRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListCallMatchersAsyncPager: + r"""Returns the list of all call matchers in the + specified conversation. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.ListCallMatchersRequest`): + The request object. The request message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2.Conversations.ListCallMatchers]. + parent (:class:`str`): + Required. The conversation to list all call matchers + from. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.services.conversations.pagers.ListCallMatchersAsyncPager: + The response message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2.Conversations.ListCallMatchers]. + + 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 = conversation.ListCallMatchersRequest(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_call_matchers, + 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.ListCallMatchersAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def delete_call_matcher( + self, + request: conversation.DeleteCallMatcherRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Requests deletion of a call matcher. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.DeleteCallMatcherRequest`): + The request object. The request message for + [Conversations.DeleteCallMatcher][google.cloud.dialogflow.v2.Conversations.DeleteCallMatcher]. + name (:class:`str`): + Required. The unique identifier of the + [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] to + delete. Format: + ``projects//locations//conversations//callMatchers/``. + + 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 = conversation.DeleteCallMatcherRequest(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_call_matcher, + 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_messages( + self, + request: conversation.ListMessagesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListMessagesAsyncPager: + r"""Lists messages that belong to a given conversation. ``messages`` + are ordered by ``create_time`` in descending order. To fetch + updates without duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.ListMessagesRequest`): + The request object. The request message for + [Conversations.ListMessages][google.cloud.dialogflow.v2.Conversations.ListMessages]. + parent (:class:`str`): + Required. The name of the conversation to list messages + for. Format: + ``projects//locations//conversations/`` + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.services.conversations.pagers.ListMessagesAsyncPager: + The response message for + [Conversations.ListMessages][google.cloud.dialogflow.v2.Conversations.ListMessages]. + + 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 = conversation.ListMessagesRequest(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_messages, + 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.ListMessagesAsyncPager( + 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ConversationsAsyncClient",) diff --git a/google/cloud/dialogflow_v2/services/conversations/client.py b/google/cloud/dialogflow_v2/services/conversations/client.py new file mode 100644 index 000000000..b03655065 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversations/client.py @@ -0,0 +1,1118 @@ +# -*- 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.dialogflow_v2.services.conversations import pagers +from google.cloud.dialogflow_v2.types import conversation +from google.cloud.dialogflow_v2.types import conversation as gcd_conversation +from google.cloud.dialogflow_v2.types import participant +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import ConversationsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import ConversationsGrpcTransport +from .transports.grpc_asyncio import ConversationsGrpcAsyncIOTransport + + +class ConversationsClientMeta(type): + """Metaclass for the Conversations 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[ConversationsTransport]] + _transport_registry["grpc"] = ConversationsGrpcTransport + _transport_registry["grpc_asyncio"] = ConversationsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[ConversationsTransport]: + """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 ConversationsClient(metaclass=ConversationsClientMeta): + """Service for managing + [Conversations][google.cloud.dialogflow.v2.Conversation]. + """ + + @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_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationsClient: 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) -> ConversationsTransport: + """Return the transport used by the client instance. + + Returns: + ConversationsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def call_matcher_path(project: str, conversation: str, call_matcher: str,) -> str: + """Return a fully-qualified call_matcher string.""" + return "projects/{project}/conversations/{conversation}/callMatchers/{call_matcher}".format( + project=project, conversation=conversation, call_matcher=call_matcher, + ) + + @staticmethod + def parse_call_matcher_path(path: str) -> Dict[str, str]: + """Parse a call_matcher path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversations/(?P.+?)/callMatchers/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def conversation_path(project: str, conversation: str,) -> str: + """Return a fully-qualified conversation string.""" + return "projects/{project}/conversations/{conversation}".format( + project=project, conversation=conversation, + ) + + @staticmethod + def parse_conversation_path(path: str) -> Dict[str, str]: + """Parse a conversation path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversations/(?P.+?)$", path + ) + return m.groupdict() if m else {} + + @staticmethod + def conversation_profile_path(project: str, conversation_profile: str,) -> str: + """Return a fully-qualified conversation_profile string.""" + return "projects/{project}/conversationProfiles/{conversation_profile}".format( + project=project, conversation_profile=conversation_profile, + ) + + @staticmethod + def parse_conversation_profile_path(path: str) -> Dict[str, str]: + """Parse a conversation_profile path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversationProfiles/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def message_path(project: str, conversation: str, message: str,) -> str: + """Return a fully-qualified message string.""" + return "projects/{project}/conversations/{conversation}/messages/{message}".format( + project=project, conversation=conversation, message=message, + ) + + @staticmethod + def parse_message_path(path: str) -> Dict[str, str]: + """Parse a message path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversations/(?P.+?)/messages/(?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, ConversationsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the conversations 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, ConversationsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, ConversationsTransport): + # transport is a ConversationsTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def create_conversation( + self, + request: gcd_conversation.CreateConversationRequest = None, + *, + parent: str = None, + conversation: gcd_conversation.Conversation = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation.Conversation: + r"""Creates a new conversation. Conversations are auto-completed + after 24 hours. + + Conversation Lifecycle: There are two stages during a + conversation: Automated Agent Stage and Assist Stage. + + For Automated Agent Stage, there will be a dialogflow agent + responding to user queries. + + For Assist Stage, there's no dialogflow agent responding to user + queries. But we will provide suggestions which are generated + from conversation. + + If + [Conversation.conversation_profile][google.cloud.dialogflow.v2.Conversation.conversation_profile] + is configured for a dialogflow agent, conversation will start + from ``Automated Agent Stage``, otherwise, it will start from + ``Assist Stage``. And during ``Automated Agent Stage``, once an + [Intent][google.cloud.dialogflow.v2.Intent] with + [Intent.live_agent_handoff][google.cloud.dialogflow.v2.Intent.live_agent_handoff] + is triggered, conversation will transfer to Assist Stage. + + Args: + request (google.cloud.dialogflow_v2.types.CreateConversationRequest): + The request object. The request message for + [Conversations.CreateConversation][google.cloud.dialogflow.v2.Conversations.CreateConversation]. + parent (str): + Required. Resource identifier of the project creating + the conversation. Format: + ``projects//locations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + conversation (google.cloud.dialogflow_v2.types.Conversation): + Required. The conversation to create. + This corresponds to the ``conversation`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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, conversation]) + if request is 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 gcd_conversation.CreateConversationRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_conversation.CreateConversationRequest): + request = gcd_conversation.CreateConversationRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if conversation is not None: + request.conversation = conversation + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_conversation] + + # 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 list_conversations( + self, + request: conversation.ListConversationsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListConversationsPager: + r"""Returns the list of all conversations in the + specified project. + + Args: + request (google.cloud.dialogflow_v2.types.ListConversationsRequest): + The request object. The request message for + [Conversations.ListConversations][google.cloud.dialogflow.v2.Conversations.ListConversations]. + parent (str): + Required. The project from which to list all + conversation. 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: + google.cloud.dialogflow_v2.services.conversations.pagers.ListConversationsPager: + The response message for + [Conversations.ListConversations][google.cloud.dialogflow.v2.Conversations.ListConversations]. + + 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 conversation.ListConversationsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.ListConversationsRequest): + request = conversation.ListConversationsRequest(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_conversations] + + # 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.ListConversationsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_conversation( + self, + request: conversation.GetConversationRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.Conversation: + r"""Retrieves the specific conversation. + + Args: + request (google.cloud.dialogflow_v2.types.GetConversationRequest): + The request object. The request message for + [Conversations.GetConversation][google.cloud.dialogflow.v2.Conversations.GetConversation]. + name (str): + Required. The name of the conversation. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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 conversation.GetConversationRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.GetConversationRequest): + request = conversation.GetConversationRequest(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_conversation] + + # 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 complete_conversation( + self, + request: conversation.CompleteConversationRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.Conversation: + r"""Completes the specified conversation. Finished + conversations are purged from the database after 30 + days. + + Args: + request (google.cloud.dialogflow_v2.types.CompleteConversationRequest): + The request object. The request message for + [Conversations.CompleteConversation][google.cloud.dialogflow.v2.Conversations.CompleteConversation]. + name (str): + Required. Resource identifier of the conversation to + close. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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 conversation.CompleteConversationRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.CompleteConversationRequest): + request = conversation.CompleteConversationRequest(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.complete_conversation] + + # 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_call_matcher( + self, + request: conversation.CreateCallMatcherRequest = None, + *, + parent: str = None, + call_matcher: conversation.CallMatcher = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.CallMatcher: + r"""Creates a call matcher that links incoming SIP calls + to the specified conversation if they fulfill specified + criteria. + + Args: + request (google.cloud.dialogflow_v2.types.CreateCallMatcherRequest): + The request object. The request message for + [Conversations.CreateCallMatcher][google.cloud.dialogflow.v2.Conversations.CreateCallMatcher]. + parent (str): + Required. Resource identifier of the conversation adding + the call matcher. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + call_matcher (google.cloud.dialogflow_v2.types.CallMatcher): + Required. The call matcher to create. + This corresponds to the ``call_matcher`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.CallMatcher: + Represents a call matcher that describes criteria for matching incoming SIP + calls to a conversation. When Dialogflow get a SIP + call from a third-party carrier, Dialogflow matches + the call to an existing conversation by either: + + - Extracting the conversation id from the [Call-Info + header](\ https://tools.ietf.org/html/rfc3261#section-20.9), + e.g. Call-Info: + + ;purpose=Goog-ContactCenter-Conversation. + + \* Or, if that doesn't work, matching incoming [SIP + headers](\ https://tools.ietf.org/html/rfc3261#section-7.3) + against any + [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] + for the conversation. + + If an incoming SIP call without valid Call-Info + header matches to zero or multiple conversations with + CallMatcher, we reject it. + + A call matcher contains equality conditions for SIP + headers that all have to be fulfilled in order for a + SIP call to match. + + The matched SIP headers consist of well-known headers + (To, From, Call-ID) and custom headers. A + [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] + is only valid if it specifies: + + - At least 1 custom header, + - or at least 2 well-known headers. + + """ + # Create or coerce a 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, call_matcher]) + if request is 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 conversation.CreateCallMatcherRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.CreateCallMatcherRequest): + request = conversation.CreateCallMatcherRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if call_matcher is not None: + request.call_matcher = call_matcher + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_call_matcher] + + # 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 list_call_matchers( + self, + request: conversation.ListCallMatchersRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListCallMatchersPager: + r"""Returns the list of all call matchers in the + specified conversation. + + Args: + request (google.cloud.dialogflow_v2.types.ListCallMatchersRequest): + The request object. The request message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2.Conversations.ListCallMatchers]. + parent (str): + Required. The conversation to list all call matchers + from. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.services.conversations.pagers.ListCallMatchersPager: + The response message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2.Conversations.ListCallMatchers]. + + 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 conversation.ListCallMatchersRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.ListCallMatchersRequest): + request = conversation.ListCallMatchersRequest(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_call_matchers] + + # 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.ListCallMatchersPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def delete_call_matcher( + self, + request: conversation.DeleteCallMatcherRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Requests deletion of a call matcher. + + Args: + request (google.cloud.dialogflow_v2.types.DeleteCallMatcherRequest): + The request object. The request message for + [Conversations.DeleteCallMatcher][google.cloud.dialogflow.v2.Conversations.DeleteCallMatcher]. + name (str): + Required. The unique identifier of the + [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] to + delete. Format: + ``projects//locations//conversations//callMatchers/``. + + 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 conversation.DeleteCallMatcherRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.DeleteCallMatcherRequest): + request = conversation.DeleteCallMatcherRequest(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_call_matcher] + + # 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_messages( + self, + request: conversation.ListMessagesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListMessagesPager: + r"""Lists messages that belong to a given conversation. ``messages`` + are ordered by ``create_time`` in descending order. To fetch + updates without duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Args: + request (google.cloud.dialogflow_v2.types.ListMessagesRequest): + The request object. The request message for + [Conversations.ListMessages][google.cloud.dialogflow.v2.Conversations.ListMessages]. + parent (str): + Required. The name of the conversation to list messages + for. Format: + ``projects//locations//conversations/`` + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.services.conversations.pagers.ListMessagesPager: + The response message for + [Conversations.ListMessages][google.cloud.dialogflow.v2.Conversations.ListMessages]. + + 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 conversation.ListMessagesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.ListMessagesRequest): + request = conversation.ListMessagesRequest(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_messages] + + # 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.ListMessagesPager( + 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ConversationsClient",) diff --git a/google/cloud/dialogflow_v2/services/conversations/pagers.py b/google/cloud/dialogflow_v2/services/conversations/pagers.py new file mode 100644 index 000000000..6e93bdbf6 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversations/pagers.py @@ -0,0 +1,414 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.dialogflow_v2.types import conversation +from google.cloud.dialogflow_v2.types import participant + + +class ListConversationsPager: + """A pager for iterating through ``list_conversations`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListConversationsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``conversations`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListConversations`` requests and continue to iterate + through the ``conversations`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListConversationsResponse` + 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[..., conversation.ListConversationsResponse], + request: conversation.ListConversationsRequest, + response: conversation.ListConversationsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListConversationsRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListConversationsResponse): + 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 = conversation.ListConversationsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[conversation.ListConversationsResponse]: + 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[conversation.Conversation]: + for page in self.pages: + yield from page.conversations + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListConversationsAsyncPager: + """A pager for iterating through ``list_conversations`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListConversationsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``conversations`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListConversations`` requests and continue to iterate + through the ``conversations`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListConversationsResponse` + 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[conversation.ListConversationsResponse]], + request: conversation.ListConversationsRequest, + response: conversation.ListConversationsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListConversationsRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListConversationsResponse): + 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 = conversation.ListConversationsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[conversation.ListConversationsResponse]: + 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[conversation.Conversation]: + async def async_generator(): + async for page in self.pages: + for response in page.conversations: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListCallMatchersPager: + """A pager for iterating through ``list_call_matchers`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListCallMatchersResponse` object, and + provides an ``__iter__`` method to iterate through its + ``call_matchers`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListCallMatchers`` requests and continue to iterate + through the ``call_matchers`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListCallMatchersResponse` + 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[..., conversation.ListCallMatchersResponse], + request: conversation.ListCallMatchersRequest, + response: conversation.ListCallMatchersResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListCallMatchersRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListCallMatchersResponse): + 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 = conversation.ListCallMatchersRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[conversation.ListCallMatchersResponse]: + 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[conversation.CallMatcher]: + for page in self.pages: + yield from page.call_matchers + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListCallMatchersAsyncPager: + """A pager for iterating through ``list_call_matchers`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListCallMatchersResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``call_matchers`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListCallMatchers`` requests and continue to iterate + through the ``call_matchers`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListCallMatchersResponse` + 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[conversation.ListCallMatchersResponse]], + request: conversation.ListCallMatchersRequest, + response: conversation.ListCallMatchersResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListCallMatchersRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListCallMatchersResponse): + 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 = conversation.ListCallMatchersRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[conversation.ListCallMatchersResponse]: + 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[conversation.CallMatcher]: + async def async_generator(): + async for page in self.pages: + for response in page.call_matchers: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListMessagesPager: + """A pager for iterating through ``list_messages`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListMessagesResponse` object, and + provides an ``__iter__`` method to iterate through its + ``messages`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListMessages`` requests and continue to iterate + through the ``messages`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListMessagesResponse` + 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[..., conversation.ListMessagesResponse], + request: conversation.ListMessagesRequest, + response: conversation.ListMessagesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListMessagesRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListMessagesResponse): + 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 = conversation.ListMessagesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[conversation.ListMessagesResponse]: + 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[participant.Message]: + for page in self.pages: + yield from page.messages + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListMessagesAsyncPager: + """A pager for iterating through ``list_messages`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListMessagesResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``messages`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListMessages`` requests and continue to iterate + through the ``messages`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListMessagesResponse` + 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[conversation.ListMessagesResponse]], + request: conversation.ListMessagesRequest, + response: conversation.ListMessagesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListMessagesRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListMessagesResponse): + 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 = conversation.ListMessagesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[conversation.ListMessagesResponse]: + 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[participant.Message]: + async def async_generator(): + async for page in self.pages: + for response in page.messages: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflow_v2/services/conversations/transports/__init__.py b/google/cloud/dialogflow_v2/services/conversations/transports/__init__.py new file mode 100644 index 000000000..6597b6d01 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversations/transports/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import ConversationsTransport +from .grpc import ConversationsGrpcTransport +from .grpc_asyncio import ConversationsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[ConversationsTransport]] +_transport_registry["grpc"] = ConversationsGrpcTransport +_transport_registry["grpc_asyncio"] = ConversationsGrpcAsyncIOTransport + +__all__ = ( + "ConversationsTransport", + "ConversationsGrpcTransport", + "ConversationsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflow_v2/services/conversations/transports/base.py b/google/cloud/dialogflow_v2/services/conversations/transports/base.py new file mode 100644 index 000000000..121fecedf --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversations/transports/base.py @@ -0,0 +1,233 @@ +# -*- 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.dialogflow_v2.types import conversation +from google.cloud.dialogflow_v2.types import conversation as gcd_conversation +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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class ConversationsTransport(abc.ABC): + """Abstract transport class for Conversations.""" + + 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_conversation: gapic_v1.method.wrap_method( + self.create_conversation, default_timeout=None, client_info=client_info, + ), + self.list_conversations: gapic_v1.method.wrap_method( + self.list_conversations, default_timeout=None, client_info=client_info, + ), + self.get_conversation: gapic_v1.method.wrap_method( + self.get_conversation, default_timeout=None, client_info=client_info, + ), + self.complete_conversation: gapic_v1.method.wrap_method( + self.complete_conversation, + default_timeout=None, + client_info=client_info, + ), + self.create_call_matcher: gapic_v1.method.wrap_method( + self.create_call_matcher, default_timeout=None, client_info=client_info, + ), + self.list_call_matchers: gapic_v1.method.wrap_method( + self.list_call_matchers, default_timeout=None, client_info=client_info, + ), + self.delete_call_matcher: gapic_v1.method.wrap_method( + self.delete_call_matcher, default_timeout=None, client_info=client_info, + ), + self.list_messages: gapic_v1.method.wrap_method( + self.list_messages, default_timeout=None, client_info=client_info, + ), + } + + @property + def create_conversation( + self, + ) -> typing.Callable[ + [gcd_conversation.CreateConversationRequest], + typing.Union[ + gcd_conversation.Conversation, + typing.Awaitable[gcd_conversation.Conversation], + ], + ]: + raise NotImplementedError() + + @property + def list_conversations( + self, + ) -> typing.Callable[ + [conversation.ListConversationsRequest], + typing.Union[ + conversation.ListConversationsResponse, + typing.Awaitable[conversation.ListConversationsResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_conversation( + self, + ) -> typing.Callable[ + [conversation.GetConversationRequest], + typing.Union[ + conversation.Conversation, typing.Awaitable[conversation.Conversation] + ], + ]: + raise NotImplementedError() + + @property + def complete_conversation( + self, + ) -> typing.Callable[ + [conversation.CompleteConversationRequest], + typing.Union[ + conversation.Conversation, typing.Awaitable[conversation.Conversation] + ], + ]: + raise NotImplementedError() + + @property + def create_call_matcher( + self, + ) -> typing.Callable[ + [conversation.CreateCallMatcherRequest], + typing.Union[ + conversation.CallMatcher, typing.Awaitable[conversation.CallMatcher] + ], + ]: + raise NotImplementedError() + + @property + def list_call_matchers( + self, + ) -> typing.Callable[ + [conversation.ListCallMatchersRequest], + typing.Union[ + conversation.ListCallMatchersResponse, + typing.Awaitable[conversation.ListCallMatchersResponse], + ], + ]: + raise NotImplementedError() + + @property + def delete_call_matcher( + self, + ) -> typing.Callable[ + [conversation.DeleteCallMatcherRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + @property + def list_messages( + self, + ) -> typing.Callable[ + [conversation.ListMessagesRequest], + typing.Union[ + conversation.ListMessagesResponse, + typing.Awaitable[conversation.ListMessagesResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("ConversationsTransport",) diff --git a/google/cloud/dialogflow_v2/services/conversations/transports/grpc.py b/google/cloud/dialogflow_v2/services/conversations/transports/grpc.py new file mode 100644 index 000000000..5664521db --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversations/transports/grpc.py @@ -0,0 +1,503 @@ +# -*- 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.dialogflow_v2.types import conversation +from google.cloud.dialogflow_v2.types import conversation as gcd_conversation +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import ConversationsTransport, DEFAULT_CLIENT_INFO + + +class ConversationsGrpcTransport(ConversationsTransport): + """gRPC backend transport for Conversations. + + Service for managing + [Conversations][google.cloud.dialogflow.v2.Conversation]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + self._stubs = {} # type: Dict[str, Callable] + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + @classmethod + def create_channel( + cls, + host: str = "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 (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def create_conversation( + self, + ) -> Callable[ + [gcd_conversation.CreateConversationRequest], gcd_conversation.Conversation + ]: + r"""Return a callable for the create conversation method over gRPC. + + Creates a new conversation. Conversations are auto-completed + after 24 hours. + + Conversation Lifecycle: There are two stages during a + conversation: Automated Agent Stage and Assist Stage. + + For Automated Agent Stage, there will be a dialogflow agent + responding to user queries. + + For Assist Stage, there's no dialogflow agent responding to user + queries. But we will provide suggestions which are generated + from conversation. + + If + [Conversation.conversation_profile][google.cloud.dialogflow.v2.Conversation.conversation_profile] + is configured for a dialogflow agent, conversation will start + from ``Automated Agent Stage``, otherwise, it will start from + ``Assist Stage``. And during ``Automated Agent Stage``, once an + [Intent][google.cloud.dialogflow.v2.Intent] with + [Intent.live_agent_handoff][google.cloud.dialogflow.v2.Intent.live_agent_handoff] + is triggered, conversation will transfer to Assist Stage. + + Returns: + Callable[[~.CreateConversationRequest], + ~.Conversation]: + 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_conversation" not in self._stubs: + self._stubs["create_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/CreateConversation", + request_serializer=gcd_conversation.CreateConversationRequest.serialize, + response_deserializer=gcd_conversation.Conversation.deserialize, + ) + return self._stubs["create_conversation"] + + @property + def list_conversations( + self, + ) -> Callable[ + [conversation.ListConversationsRequest], conversation.ListConversationsResponse + ]: + r"""Return a callable for the list conversations method over gRPC. + + Returns the list of all conversations in the + specified project. + + Returns: + Callable[[~.ListConversationsRequest], + ~.ListConversationsResponse]: + 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_conversations" not in self._stubs: + self._stubs["list_conversations"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/ListConversations", + request_serializer=conversation.ListConversationsRequest.serialize, + response_deserializer=conversation.ListConversationsResponse.deserialize, + ) + return self._stubs["list_conversations"] + + @property + def get_conversation( + self, + ) -> Callable[[conversation.GetConversationRequest], conversation.Conversation]: + r"""Return a callable for the get conversation method over gRPC. + + Retrieves the specific conversation. + + Returns: + Callable[[~.GetConversationRequest], + ~.Conversation]: + 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_conversation" not in self._stubs: + self._stubs["get_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/GetConversation", + request_serializer=conversation.GetConversationRequest.serialize, + response_deserializer=conversation.Conversation.deserialize, + ) + return self._stubs["get_conversation"] + + @property + def complete_conversation( + self, + ) -> Callable[ + [conversation.CompleteConversationRequest], conversation.Conversation + ]: + r"""Return a callable for the complete conversation method over gRPC. + + Completes the specified conversation. Finished + conversations are purged from the database after 30 + days. + + Returns: + Callable[[~.CompleteConversationRequest], + ~.Conversation]: + 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 "complete_conversation" not in self._stubs: + self._stubs["complete_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/CompleteConversation", + request_serializer=conversation.CompleteConversationRequest.serialize, + response_deserializer=conversation.Conversation.deserialize, + ) + return self._stubs["complete_conversation"] + + @property + def create_call_matcher( + self, + ) -> Callable[[conversation.CreateCallMatcherRequest], conversation.CallMatcher]: + r"""Return a callable for the create call matcher method over gRPC. + + Creates a call matcher that links incoming SIP calls + to the specified conversation if they fulfill specified + criteria. + + Returns: + Callable[[~.CreateCallMatcherRequest], + ~.CallMatcher]: + 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_call_matcher" not in self._stubs: + self._stubs["create_call_matcher"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/CreateCallMatcher", + request_serializer=conversation.CreateCallMatcherRequest.serialize, + response_deserializer=conversation.CallMatcher.deserialize, + ) + return self._stubs["create_call_matcher"] + + @property + def list_call_matchers( + self, + ) -> Callable[ + [conversation.ListCallMatchersRequest], conversation.ListCallMatchersResponse + ]: + r"""Return a callable for the list call matchers method over gRPC. + + Returns the list of all call matchers in the + specified conversation. + + Returns: + Callable[[~.ListCallMatchersRequest], + ~.ListCallMatchersResponse]: + 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_call_matchers" not in self._stubs: + self._stubs["list_call_matchers"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/ListCallMatchers", + request_serializer=conversation.ListCallMatchersRequest.serialize, + response_deserializer=conversation.ListCallMatchersResponse.deserialize, + ) + return self._stubs["list_call_matchers"] + + @property + def delete_call_matcher( + self, + ) -> Callable[[conversation.DeleteCallMatcherRequest], empty.Empty]: + r"""Return a callable for the delete call matcher method over gRPC. + + Requests deletion of a call matcher. + + Returns: + Callable[[~.DeleteCallMatcherRequest], + ~.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_call_matcher" not in self._stubs: + self._stubs["delete_call_matcher"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/DeleteCallMatcher", + request_serializer=conversation.DeleteCallMatcherRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_call_matcher"] + + @property + def list_messages( + self, + ) -> Callable[ + [conversation.ListMessagesRequest], conversation.ListMessagesResponse + ]: + r"""Return a callable for the list messages method over gRPC. + + Lists messages that belong to a given conversation. ``messages`` + are ordered by ``create_time`` in descending order. To fetch + updates without duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Returns: + Callable[[~.ListMessagesRequest], + ~.ListMessagesResponse]: + 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_messages" not in self._stubs: + self._stubs["list_messages"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/ListMessages", + request_serializer=conversation.ListMessagesRequest.serialize, + response_deserializer=conversation.ListMessagesResponse.deserialize, + ) + return self._stubs["list_messages"] + + +__all__ = ("ConversationsGrpcTransport",) diff --git a/google/cloud/dialogflow_v2/services/conversations/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/conversations/transports/grpc_asyncio.py new file mode 100644 index 000000000..8e0f7f3ed --- /dev/null +++ b/google/cloud/dialogflow_v2/services/conversations/transports/grpc_asyncio.py @@ -0,0 +1,514 @@ +# -*- 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.dialogflow_v2.types import conversation +from google.cloud.dialogflow_v2.types import conversation as gcd_conversation +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import ConversationsTransport, DEFAULT_CLIENT_INFO +from .grpc import ConversationsGrpcTransport + + +class ConversationsGrpcAsyncIOTransport(ConversationsTransport): + """gRPC AsyncIO backend transport for Conversations. + + Service for managing + [Conversations][google.cloud.dialogflow.v2.Conversation]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + self._stubs = {} + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def create_conversation( + self, + ) -> Callable[ + [gcd_conversation.CreateConversationRequest], + Awaitable[gcd_conversation.Conversation], + ]: + r"""Return a callable for the create conversation method over gRPC. + + Creates a new conversation. Conversations are auto-completed + after 24 hours. + + Conversation Lifecycle: There are two stages during a + conversation: Automated Agent Stage and Assist Stage. + + For Automated Agent Stage, there will be a dialogflow agent + responding to user queries. + + For Assist Stage, there's no dialogflow agent responding to user + queries. But we will provide suggestions which are generated + from conversation. + + If + [Conversation.conversation_profile][google.cloud.dialogflow.v2.Conversation.conversation_profile] + is configured for a dialogflow agent, conversation will start + from ``Automated Agent Stage``, otherwise, it will start from + ``Assist Stage``. And during ``Automated Agent Stage``, once an + [Intent][google.cloud.dialogflow.v2.Intent] with + [Intent.live_agent_handoff][google.cloud.dialogflow.v2.Intent.live_agent_handoff] + is triggered, conversation will transfer to Assist Stage. + + Returns: + Callable[[~.CreateConversationRequest], + Awaitable[~.Conversation]]: + 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_conversation" not in self._stubs: + self._stubs["create_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/CreateConversation", + request_serializer=gcd_conversation.CreateConversationRequest.serialize, + response_deserializer=gcd_conversation.Conversation.deserialize, + ) + return self._stubs["create_conversation"] + + @property + def list_conversations( + self, + ) -> Callable[ + [conversation.ListConversationsRequest], + Awaitable[conversation.ListConversationsResponse], + ]: + r"""Return a callable for the list conversations method over gRPC. + + Returns the list of all conversations in the + specified project. + + Returns: + Callable[[~.ListConversationsRequest], + Awaitable[~.ListConversationsResponse]]: + 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_conversations" not in self._stubs: + self._stubs["list_conversations"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/ListConversations", + request_serializer=conversation.ListConversationsRequest.serialize, + response_deserializer=conversation.ListConversationsResponse.deserialize, + ) + return self._stubs["list_conversations"] + + @property + def get_conversation( + self, + ) -> Callable[ + [conversation.GetConversationRequest], Awaitable[conversation.Conversation] + ]: + r"""Return a callable for the get conversation method over gRPC. + + Retrieves the specific conversation. + + Returns: + Callable[[~.GetConversationRequest], + Awaitable[~.Conversation]]: + 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_conversation" not in self._stubs: + self._stubs["get_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/GetConversation", + request_serializer=conversation.GetConversationRequest.serialize, + response_deserializer=conversation.Conversation.deserialize, + ) + return self._stubs["get_conversation"] + + @property + def complete_conversation( + self, + ) -> Callable[ + [conversation.CompleteConversationRequest], Awaitable[conversation.Conversation] + ]: + r"""Return a callable for the complete conversation method over gRPC. + + Completes the specified conversation. Finished + conversations are purged from the database after 30 + days. + + Returns: + Callable[[~.CompleteConversationRequest], + Awaitable[~.Conversation]]: + 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 "complete_conversation" not in self._stubs: + self._stubs["complete_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/CompleteConversation", + request_serializer=conversation.CompleteConversationRequest.serialize, + response_deserializer=conversation.Conversation.deserialize, + ) + return self._stubs["complete_conversation"] + + @property + def create_call_matcher( + self, + ) -> Callable[ + [conversation.CreateCallMatcherRequest], Awaitable[conversation.CallMatcher] + ]: + r"""Return a callable for the create call matcher method over gRPC. + + Creates a call matcher that links incoming SIP calls + to the specified conversation if they fulfill specified + criteria. + + Returns: + Callable[[~.CreateCallMatcherRequest], + Awaitable[~.CallMatcher]]: + 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_call_matcher" not in self._stubs: + self._stubs["create_call_matcher"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/CreateCallMatcher", + request_serializer=conversation.CreateCallMatcherRequest.serialize, + response_deserializer=conversation.CallMatcher.deserialize, + ) + return self._stubs["create_call_matcher"] + + @property + def list_call_matchers( + self, + ) -> Callable[ + [conversation.ListCallMatchersRequest], + Awaitable[conversation.ListCallMatchersResponse], + ]: + r"""Return a callable for the list call matchers method over gRPC. + + Returns the list of all call matchers in the + specified conversation. + + Returns: + Callable[[~.ListCallMatchersRequest], + Awaitable[~.ListCallMatchersResponse]]: + 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_call_matchers" not in self._stubs: + self._stubs["list_call_matchers"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/ListCallMatchers", + request_serializer=conversation.ListCallMatchersRequest.serialize, + response_deserializer=conversation.ListCallMatchersResponse.deserialize, + ) + return self._stubs["list_call_matchers"] + + @property + def delete_call_matcher( + self, + ) -> Callable[[conversation.DeleteCallMatcherRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete call matcher method over gRPC. + + Requests deletion of a call matcher. + + Returns: + Callable[[~.DeleteCallMatcherRequest], + 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_call_matcher" not in self._stubs: + self._stubs["delete_call_matcher"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/DeleteCallMatcher", + request_serializer=conversation.DeleteCallMatcherRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_call_matcher"] + + @property + def list_messages( + self, + ) -> Callable[ + [conversation.ListMessagesRequest], Awaitable[conversation.ListMessagesResponse] + ]: + r"""Return a callable for the list messages method over gRPC. + + Lists messages that belong to a given conversation. ``messages`` + are ordered by ``create_time`` in descending order. To fetch + updates without duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Returns: + Callable[[~.ListMessagesRequest], + Awaitable[~.ListMessagesResponse]]: + 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_messages" not in self._stubs: + self._stubs["list_messages"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Conversations/ListMessages", + request_serializer=conversation.ListMessagesRequest.serialize, + response_deserializer=conversation.ListMessagesResponse.deserialize, + ) + return self._stubs["list_messages"] + + +__all__ = ("ConversationsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflow_v2/services/documents/__init__.py b/google/cloud/dialogflow_v2/services/documents/__init__.py new file mode 100644 index 000000000..5f6db6815 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/documents/__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 DocumentsClient +from .async_client import DocumentsAsyncClient + +__all__ = ( + "DocumentsClient", + "DocumentsAsyncClient", +) diff --git a/google/cloud/dialogflow_v2/services/documents/async_client.py b/google/cloud/dialogflow_v2/services/documents/async_client.py new file mode 100644 index 000000000..62632ef32 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/documents/async_client.py @@ -0,0 +1,774 @@ +# -*- 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.dialogflow_v2.services.documents import pagers +from google.cloud.dialogflow_v2.types import document +from google.cloud.dialogflow_v2.types import document as gcd_document +from google.protobuf import empty_pb2 as empty # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import DocumentsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import DocumentsGrpcAsyncIOTransport +from .client import DocumentsClient + + +class DocumentsAsyncClient: + """Service for managing knowledge + [Documents][google.cloud.dialogflow.v2.Document]. + """ + + _client: DocumentsClient + + DEFAULT_ENDPOINT = DocumentsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = DocumentsClient.DEFAULT_MTLS_ENDPOINT + + document_path = staticmethod(DocumentsClient.document_path) + parse_document_path = staticmethod(DocumentsClient.parse_document_path) + + common_billing_account_path = staticmethod( + DocumentsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + DocumentsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(DocumentsClient.common_folder_path) + parse_common_folder_path = staticmethod(DocumentsClient.parse_common_folder_path) + + common_organization_path = staticmethod(DocumentsClient.common_organization_path) + parse_common_organization_path = staticmethod( + DocumentsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(DocumentsClient.common_project_path) + parse_common_project_path = staticmethod(DocumentsClient.parse_common_project_path) + + common_location_path = staticmethod(DocumentsClient.common_location_path) + parse_common_location_path = staticmethod( + DocumentsClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + DocumentsAsyncClient: The constructed client. + """ + return DocumentsClient.from_service_account_info.__func__(DocumentsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + DocumentsAsyncClient: The constructed client. + """ + return DocumentsClient.from_service_account_file.__func__(DocumentsAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> DocumentsTransport: + """Return the transport used by the client instance. + + Returns: + DocumentsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(DocumentsClient).get_transport_class, type(DocumentsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, DocumentsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the documents 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, ~.DocumentsTransport]): 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 = DocumentsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_documents( + self, + request: document.ListDocumentsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListDocumentsAsyncPager: + r"""Returns the list of all documents of the knowledge + base. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.ListDocumentsRequest`): + The request object. Request message for + [Documents.ListDocuments][google.cloud.dialogflow.v2.Documents.ListDocuments]. + parent (:class:`str`): + Required. The knowledge base to list all documents for. + Format: + ``projects//locations//knowledgeBases/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.services.documents.pagers.ListDocumentsAsyncPager: + Response message for + [Documents.ListDocuments][google.cloud.dialogflow.v2.Documents.ListDocuments]. + + 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 = document.ListDocumentsRequest(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_documents, + 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.ListDocumentsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_document( + self, + request: document.GetDocumentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> document.Document: + r"""Retrieves the specified document. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.GetDocumentRequest`): + The request object. Request message for + [Documents.GetDocument][google.cloud.dialogflow.v2.Documents.GetDocument]. + name (:class:`str`): + Required. The name of the document to retrieve. Format + ``projects//locations//knowledgeBases//documents/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Document: + A knowledge document to be used by a + [KnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBase]. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. + + """ + # Create or coerce a 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 = document.GetDocumentRequest(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_document, + 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_document( + self, + request: gcd_document.CreateDocumentRequest = None, + *, + parent: str = None, + document: gcd_document.Document = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Creates a new document. + + Operation + + Args: + request (:class:`google.cloud.dialogflow_v2.types.CreateDocumentRequest`): + The request object. Request message for + [Documents.CreateDocument][google.cloud.dialogflow.v2.Documents.CreateDocument]. + parent (:class:`str`): + Required. The knowledge base to create a document for. + Format: + ``projects//locations//knowledgeBases/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + document (:class:`google.cloud.dialogflow_v2.types.Document`): + Required. The document to create. + This corresponds to the ``document`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.dialogflow_v2.types.Document` A + knowledge document to be used by a + [KnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBase]. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. + + """ + # Create or coerce a 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, document]) + if request is 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 = gcd_document.CreateDocumentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if document is not None: + request.document = document + + # 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_document, + 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, + gcd_document.Document, + metadata_type=gcd_document.KnowledgeOperationMetadata, + ) + + # Done; return the response. + return response + + async def delete_document( + self, + request: document.DeleteDocumentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Deletes the specified document. + + Operation + + Args: + request (:class:`google.cloud.dialogflow_v2.types.DeleteDocumentRequest`): + The request object. Request message for + [Documents.DeleteDocument][google.cloud.dialogflow.v2.Documents.DeleteDocument]. + name (:class:`str`): + Required. The name of the document to delete. Format: + ``projects//locations//knowledgeBases//documents/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: + + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); + + } + + The JSON representation for Empty is empty JSON + object {}. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + request = document.DeleteDocumentRequest(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_document, + 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=document.KnowledgeOperationMetadata, + ) + + # Done; return the response. + return response + + async def update_document( + self, + request: gcd_document.UpdateDocumentRequest = None, + *, + document: gcd_document.Document = 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 document. + + Operation + + Args: + request (:class:`google.cloud.dialogflow_v2.types.UpdateDocumentRequest`): + The request object. Request message for + [Documents.UpdateDocument][google.cloud.dialogflow.v2.Documents.UpdateDocument]. + document (:class:`google.cloud.dialogflow_v2.types.Document`): + Required. The document to update. + This corresponds to the ``document`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): + Optional. Not specified means ``update all``. Currently, + only ``display_name`` can be updated, an InvalidArgument + will be returned for attempting to update other fields. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.dialogflow_v2.types.Document` A + knowledge document to be used by a + [KnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBase]. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. + + """ + # Create or coerce a 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([document, 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 = gcd_document.UpdateDocumentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if document is not None: + request.document = document + 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_document, + 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( + (("document.name", request.document.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, + gcd_document.Document, + metadata_type=gcd_document.KnowledgeOperationMetadata, + ) + + # Done; return the response. + return response + + async def reload_document( + self, + request: document.ReloadDocumentRequest = None, + *, + name: str = None, + content_uri: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Reloads the specified document from its specified source, + content_uri or content. The previously loaded content of the + document will be deleted. Note: Even when the content of the + document has not changed, there still may be side effects + because of internal implementation changes. + + Note: The ``projects.agent.knowledgeBases.documents`` resource + is deprecated; only use ``projects.knowledgeBases.documents``. + + Operation + + Args: + request (:class:`google.cloud.dialogflow_v2.types.ReloadDocumentRequest`): + The request object. Request message for + [Documents.ReloadDocument][google.cloud.dialogflow.v2.Documents.ReloadDocument]. + name (:class:`str`): + Required. The name of the document to reload. Format: + ``projects//locations//knowledgeBases//documents/`` + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + content_uri (:class:`str`): + Optional. The path of gcs source file for reloading + document content. For now, only gcs uri is supported. + + For documents stored in Google Cloud Storage, these URIs + must have the form ``gs:///``. + + This corresponds to the ``content_uri`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.dialogflow_v2.types.Document` A + knowledge document to be used by a + [KnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBase]. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. + + """ + # Create or coerce a 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, content_uri]) + if request is 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 = document.ReloadDocumentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + if content_uri is not None: + request.content_uri = content_uri + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.reload_document, + 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, + document.Document, + metadata_type=document.KnowledgeOperationMetadata, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("DocumentsAsyncClient",) diff --git a/google/cloud/dialogflow_v2/services/documents/client.py b/google/cloud/dialogflow_v2/services/documents/client.py new file mode 100644 index 000000000..3863502ed --- /dev/null +++ b/google/cloud/dialogflow_v2/services/documents/client.py @@ -0,0 +1,958 @@ +# -*- 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.dialogflow_v2.services.documents import pagers +from google.cloud.dialogflow_v2.types import document +from google.cloud.dialogflow_v2.types import document as gcd_document +from google.protobuf import empty_pb2 as empty # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import DocumentsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import DocumentsGrpcTransport +from .transports.grpc_asyncio import DocumentsGrpcAsyncIOTransport + + +class DocumentsClientMeta(type): + """Metaclass for the Documents 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[DocumentsTransport]] + _transport_registry["grpc"] = DocumentsGrpcTransport + _transport_registry["grpc_asyncio"] = DocumentsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[DocumentsTransport]: + """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 DocumentsClient(metaclass=DocumentsClientMeta): + """Service for managing knowledge + [Documents][google.cloud.dialogflow.v2.Document]. + """ + + @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_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + DocumentsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + DocumentsClient: 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) -> DocumentsTransport: + """Return the transport used by the client instance. + + Returns: + DocumentsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def document_path(project: str, knowledge_base: str, document: str,) -> str: + """Return a fully-qualified document string.""" + return "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, knowledge_base=knowledge_base, document=document, + ) + + @staticmethod + def parse_document_path(path: str) -> Dict[str, str]: + """Parse a document path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/knowledgeBases/(?P.+?)/documents/(?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, DocumentsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the documents 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, DocumentsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, DocumentsTransport): + # transport is a DocumentsTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_documents( + self, + request: document.ListDocumentsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListDocumentsPager: + r"""Returns the list of all documents of the knowledge + base. + + Args: + request (google.cloud.dialogflow_v2.types.ListDocumentsRequest): + The request object. Request message for + [Documents.ListDocuments][google.cloud.dialogflow.v2.Documents.ListDocuments]. + parent (str): + Required. The knowledge base to list all documents for. + Format: + ``projects//locations//knowledgeBases/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.services.documents.pagers.ListDocumentsPager: + Response message for + [Documents.ListDocuments][google.cloud.dialogflow.v2.Documents.ListDocuments]. + + 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 document.ListDocumentsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, document.ListDocumentsRequest): + request = document.ListDocumentsRequest(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_documents] + + # 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.ListDocumentsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_document( + self, + request: document.GetDocumentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> document.Document: + r"""Retrieves the specified document. + + Args: + request (google.cloud.dialogflow_v2.types.GetDocumentRequest): + The request object. Request message for + [Documents.GetDocument][google.cloud.dialogflow.v2.Documents.GetDocument]. + name (str): + Required. The name of the document to retrieve. Format + ``projects//locations//knowledgeBases//documents/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Document: + A knowledge document to be used by a + [KnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBase]. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. + + """ + # Create or coerce a 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 document.GetDocumentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, document.GetDocumentRequest): + request = document.GetDocumentRequest(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_document] + + # 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_document( + self, + request: gcd_document.CreateDocumentRequest = None, + *, + parent: str = None, + document: gcd_document.Document = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Creates a new document. + + Operation + + Args: + request (google.cloud.dialogflow_v2.types.CreateDocumentRequest): + The request object. Request message for + [Documents.CreateDocument][google.cloud.dialogflow.v2.Documents.CreateDocument]. + parent (str): + Required. The knowledge base to create a document for. + Format: + ``projects//locations//knowledgeBases/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + document (google.cloud.dialogflow_v2.types.Document): + Required. The document to create. + This corresponds to the ``document`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.dialogflow_v2.types.Document` A + knowledge document to be used by a + [KnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBase]. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. + + """ + # Create or coerce a 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, document]) + if request is 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 gcd_document.CreateDocumentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_document.CreateDocumentRequest): + request = gcd_document.CreateDocumentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if document is not None: + request.document = document + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_document] + + # 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, + gcd_document.Document, + metadata_type=gcd_document.KnowledgeOperationMetadata, + ) + + # Done; return the response. + return response + + def delete_document( + self, + request: document.DeleteDocumentRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Deletes the specified document. + + Operation + + Args: + request (google.cloud.dialogflow_v2.types.DeleteDocumentRequest): + The request object. Request message for + [Documents.DeleteDocument][google.cloud.dialogflow.v2.Documents.DeleteDocument]. + name (str): + Required. The name of the document to delete. Format: + ``projects//locations//knowledgeBases//documents/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: + + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); + + } + + The JSON representation for Empty is empty JSON + object {}. + + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a document.DeleteDocumentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, document.DeleteDocumentRequest): + request = document.DeleteDocumentRequest(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_document] + + # 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=document.KnowledgeOperationMetadata, + ) + + # Done; return the response. + return response + + def update_document( + self, + request: gcd_document.UpdateDocumentRequest = None, + *, + document: gcd_document.Document = 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 document. + + Operation + + Args: + request (google.cloud.dialogflow_v2.types.UpdateDocumentRequest): + The request object. Request message for + [Documents.UpdateDocument][google.cloud.dialogflow.v2.Documents.UpdateDocument]. + document (google.cloud.dialogflow_v2.types.Document): + Required. The document to update. + This corresponds to the ``document`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Optional. Not specified means ``update all``. Currently, + only ``display_name`` can be updated, an InvalidArgument + will be returned for attempting to update other fields. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.dialogflow_v2.types.Document` A + knowledge document to be used by a + [KnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBase]. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. + + """ + # Create or coerce a 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([document, 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 gcd_document.UpdateDocumentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_document.UpdateDocumentRequest): + request = gcd_document.UpdateDocumentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if document is not None: + request.document = document + 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_document] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("document.name", request.document.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, + gcd_document.Document, + metadata_type=gcd_document.KnowledgeOperationMetadata, + ) + + # Done; return the response. + return response + + def reload_document( + self, + request: document.ReloadDocumentRequest = None, + *, + name: str = None, + content_uri: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Reloads the specified document from its specified source, + content_uri or content. The previously loaded content of the + document will be deleted. Note: Even when the content of the + document has not changed, there still may be side effects + because of internal implementation changes. + + Note: The ``projects.agent.knowledgeBases.documents`` resource + is deprecated; only use ``projects.knowledgeBases.documents``. + + Operation + + Args: + request (google.cloud.dialogflow_v2.types.ReloadDocumentRequest): + The request object. Request message for + [Documents.ReloadDocument][google.cloud.dialogflow.v2.Documents.ReloadDocument]. + name (str): + Required. The name of the document to reload. Format: + ``projects//locations//knowledgeBases//documents/`` + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + content_uri (str): + Optional. The path of gcs source file for reloading + document content. For now, only gcs uri is supported. + + For documents stored in Google Cloud Storage, these URIs + must have the form ``gs:///``. + + This corresponds to the ``content_uri`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.dialogflow_v2.types.Document` A + knowledge document to be used by a + [KnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBase]. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. + + """ + # Create or coerce a 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, content_uri]) + if request is 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 document.ReloadDocumentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, document.ReloadDocumentRequest): + request = document.ReloadDocumentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if name is not None: + request.name = name + if content_uri is not None: + request.content_uri = content_uri + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.reload_document] + + # 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, + document.Document, + metadata_type=document.KnowledgeOperationMetadata, + ) + + # Done; return the response. + return response + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("DocumentsClient",) diff --git a/google/cloud/dialogflow_v2/services/documents/pagers.py b/google/cloud/dialogflow_v2/services/documents/pagers.py new file mode 100644 index 000000000..9c5ec068a --- /dev/null +++ b/google/cloud/dialogflow_v2/services/documents/pagers.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.dialogflow_v2.types import document + + +class ListDocumentsPager: + """A pager for iterating through ``list_documents`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListDocumentsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``documents`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListDocuments`` requests and continue to iterate + through the ``documents`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListDocumentsResponse` + 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[..., document.ListDocumentsResponse], + request: document.ListDocumentsRequest, + response: document.ListDocumentsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListDocumentsRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListDocumentsResponse): + 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 = document.ListDocumentsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[document.ListDocumentsResponse]: + 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[document.Document]: + for page in self.pages: + yield from page.documents + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListDocumentsAsyncPager: + """A pager for iterating through ``list_documents`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListDocumentsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``documents`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListDocuments`` requests and continue to iterate + through the ``documents`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListDocumentsResponse` + 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[document.ListDocumentsResponse]], + request: document.ListDocumentsRequest, + response: document.ListDocumentsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListDocumentsRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListDocumentsResponse): + 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 = document.ListDocumentsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[document.ListDocumentsResponse]: + 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[document.Document]: + async def async_generator(): + async for page in self.pages: + for response in page.documents: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflow_v2/services/documents/transports/__init__.py b/google/cloud/dialogflow_v2/services/documents/transports/__init__.py new file mode 100644 index 000000000..af02ba19c --- /dev/null +++ b/google/cloud/dialogflow_v2/services/documents/transports/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import DocumentsTransport +from .grpc import DocumentsGrpcTransport +from .grpc_asyncio import DocumentsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[DocumentsTransport]] +_transport_registry["grpc"] = DocumentsGrpcTransport +_transport_registry["grpc_asyncio"] = DocumentsGrpcAsyncIOTransport + +__all__ = ( + "DocumentsTransport", + "DocumentsGrpcTransport", + "DocumentsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflow_v2/services/documents/transports/base.py b/google/cloud/dialogflow_v2/services/documents/transports/base.py new file mode 100644 index 000000000..ed8d58937 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/documents/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.dialogflow_v2.types import document +from google.cloud.dialogflow_v2.types import document as gcd_document +from google.longrunning import operations_pb2 as operations # type: ignore + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class DocumentsTransport(abc.ABC): + """Abstract transport class for Documents.""" + + 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_documents: gapic_v1.method.wrap_method( + self.list_documents, default_timeout=None, client_info=client_info, + ), + self.get_document: gapic_v1.method.wrap_method( + self.get_document, default_timeout=None, client_info=client_info, + ), + self.create_document: gapic_v1.method.wrap_method( + self.create_document, default_timeout=None, client_info=client_info, + ), + self.delete_document: gapic_v1.method.wrap_method( + self.delete_document, default_timeout=None, client_info=client_info, + ), + self.update_document: gapic_v1.method.wrap_method( + self.update_document, default_timeout=None, client_info=client_info, + ), + self.reload_document: gapic_v1.method.wrap_method( + self.reload_document, 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_documents( + self, + ) -> typing.Callable[ + [document.ListDocumentsRequest], + typing.Union[ + document.ListDocumentsResponse, + typing.Awaitable[document.ListDocumentsResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_document( + self, + ) -> typing.Callable[ + [document.GetDocumentRequest], + typing.Union[document.Document, typing.Awaitable[document.Document]], + ]: + raise NotImplementedError() + + @property + def create_document( + self, + ) -> typing.Callable[ + [gcd_document.CreateDocumentRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + + @property + def delete_document( + self, + ) -> typing.Callable[ + [document.DeleteDocumentRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + + @property + def update_document( + self, + ) -> typing.Callable[ + [gcd_document.UpdateDocumentRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + + @property + def reload_document( + self, + ) -> typing.Callable[ + [document.ReloadDocumentRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + + +__all__ = ("DocumentsTransport",) diff --git a/google/cloud/dialogflow_v2/services/documents/transports/grpc.py b/google/cloud/dialogflow_v2/services/documents/transports/grpc.py new file mode 100644 index 000000000..9f6d6f9ac --- /dev/null +++ b/google/cloud/dialogflow_v2/services/documents/transports/grpc.py @@ -0,0 +1,451 @@ +# -*- 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.dialogflow_v2.types import document +from google.cloud.dialogflow_v2.types import document as gcd_document +from google.longrunning import operations_pb2 as operations # type: ignore + +from .base import DocumentsTransport, DEFAULT_CLIENT_INFO + + +class DocumentsGrpcTransport(DocumentsTransport): + """gRPC backend transport for Documents. + + Service for managing knowledge + [Documents][google.cloud.dialogflow.v2.Document]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + self._stubs = {} # type: Dict[str, Callable] + self._operations_client = None + + # 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 (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsClient(self.grpc_channel) + + # Return the client from cache. + return self._operations_client + + @property + def list_documents( + self, + ) -> Callable[[document.ListDocumentsRequest], document.ListDocumentsResponse]: + r"""Return a callable for the list documents method over gRPC. + + Returns the list of all documents of the knowledge + base. + + Returns: + Callable[[~.ListDocumentsRequest], + ~.ListDocumentsResponse]: + 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_documents" not in self._stubs: + self._stubs["list_documents"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/ListDocuments", + request_serializer=document.ListDocumentsRequest.serialize, + response_deserializer=document.ListDocumentsResponse.deserialize, + ) + return self._stubs["list_documents"] + + @property + def get_document( + self, + ) -> Callable[[document.GetDocumentRequest], document.Document]: + r"""Return a callable for the get document method over gRPC. + + Retrieves the specified document. + + Returns: + Callable[[~.GetDocumentRequest], + ~.Document]: + 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_document" not in self._stubs: + self._stubs["get_document"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/GetDocument", + request_serializer=document.GetDocumentRequest.serialize, + response_deserializer=document.Document.deserialize, + ) + return self._stubs["get_document"] + + @property + def create_document( + self, + ) -> Callable[[gcd_document.CreateDocumentRequest], operations.Operation]: + r"""Return a callable for the create document method over gRPC. + + Creates a new document. + + Operation + + Returns: + Callable[[~.CreateDocumentRequest], + ~.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_document" not in self._stubs: + self._stubs["create_document"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/CreateDocument", + request_serializer=gcd_document.CreateDocumentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["create_document"] + + @property + def delete_document( + self, + ) -> Callable[[document.DeleteDocumentRequest], operations.Operation]: + r"""Return a callable for the delete document method over gRPC. + + Deletes the specified document. + + Operation + + Returns: + Callable[[~.DeleteDocumentRequest], + ~.Operation]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_document" not in self._stubs: + self._stubs["delete_document"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/DeleteDocument", + request_serializer=document.DeleteDocumentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["delete_document"] + + @property + def update_document( + self, + ) -> Callable[[gcd_document.UpdateDocumentRequest], operations.Operation]: + r"""Return a callable for the update document method over gRPC. + + Updates the specified document. + + Operation + + Returns: + Callable[[~.UpdateDocumentRequest], + ~.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_document" not in self._stubs: + self._stubs["update_document"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/UpdateDocument", + request_serializer=gcd_document.UpdateDocumentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["update_document"] + + @property + def reload_document( + self, + ) -> Callable[[document.ReloadDocumentRequest], operations.Operation]: + r"""Return a callable for the reload document method over gRPC. + + Reloads the specified document from its specified source, + content_uri or content. The previously loaded content of the + document will be deleted. Note: Even when the content of the + document has not changed, there still may be side effects + because of internal implementation changes. + + Note: The ``projects.agent.knowledgeBases.documents`` resource + is deprecated; only use ``projects.knowledgeBases.documents``. + + Operation + + Returns: + Callable[[~.ReloadDocumentRequest], + ~.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 "reload_document" not in self._stubs: + self._stubs["reload_document"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/ReloadDocument", + request_serializer=document.ReloadDocumentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["reload_document"] + + +__all__ = ("DocumentsGrpcTransport",) diff --git a/google/cloud/dialogflow_v2/services/documents/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/documents/transports/grpc_asyncio.py new file mode 100644 index 000000000..0d762777c --- /dev/null +++ b/google/cloud/dialogflow_v2/services/documents/transports/grpc_asyncio.py @@ -0,0 +1,463 @@ +# -*- 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.dialogflow_v2.types import document +from google.cloud.dialogflow_v2.types import document as gcd_document +from google.longrunning import operations_pb2 as operations # type: ignore + +from .base import DocumentsTransport, DEFAULT_CLIENT_INFO +from .grpc import DocumentsGrpcTransport + + +class DocumentsGrpcAsyncIOTransport(DocumentsTransport): + """gRPC AsyncIO backend transport for Documents. + + Service for managing knowledge + [Documents][google.cloud.dialogflow.v2.Document]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + self._stubs = {} + self._operations_client = None + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def operations_client(self) -> operations_v1.OperationsAsyncClient: + """Create the client designed to process long-running operations. + + This property caches on the instance; repeated calls return the same + client. + """ + # Sanity check: Only create a new client if we do not already have one. + if self._operations_client is None: + self._operations_client = operations_v1.OperationsAsyncClient( + self.grpc_channel + ) + + # Return the client from cache. + return self._operations_client + + @property + def list_documents( + self, + ) -> Callable[ + [document.ListDocumentsRequest], Awaitable[document.ListDocumentsResponse] + ]: + r"""Return a callable for the list documents method over gRPC. + + Returns the list of all documents of the knowledge + base. + + Returns: + Callable[[~.ListDocumentsRequest], + Awaitable[~.ListDocumentsResponse]]: + 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_documents" not in self._stubs: + self._stubs["list_documents"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/ListDocuments", + request_serializer=document.ListDocumentsRequest.serialize, + response_deserializer=document.ListDocumentsResponse.deserialize, + ) + return self._stubs["list_documents"] + + @property + def get_document( + self, + ) -> Callable[[document.GetDocumentRequest], Awaitable[document.Document]]: + r"""Return a callable for the get document method over gRPC. + + Retrieves the specified document. + + Returns: + Callable[[~.GetDocumentRequest], + Awaitable[~.Document]]: + 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_document" not in self._stubs: + self._stubs["get_document"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/GetDocument", + request_serializer=document.GetDocumentRequest.serialize, + response_deserializer=document.Document.deserialize, + ) + return self._stubs["get_document"] + + @property + def create_document( + self, + ) -> Callable[ + [gcd_document.CreateDocumentRequest], Awaitable[operations.Operation] + ]: + r"""Return a callable for the create document method over gRPC. + + Creates a new document. + + Operation + + Returns: + Callable[[~.CreateDocumentRequest], + 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_document" not in self._stubs: + self._stubs["create_document"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/CreateDocument", + request_serializer=gcd_document.CreateDocumentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["create_document"] + + @property + def delete_document( + self, + ) -> Callable[[document.DeleteDocumentRequest], Awaitable[operations.Operation]]: + r"""Return a callable for the delete document method over gRPC. + + Deletes the specified document. + + Operation + + Returns: + Callable[[~.DeleteDocumentRequest], + Awaitable[~.Operation]]: + A function that, when called, will call the underlying RPC + on the server. + """ + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_document" not in self._stubs: + self._stubs["delete_document"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/DeleteDocument", + request_serializer=document.DeleteDocumentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["delete_document"] + + @property + def update_document( + self, + ) -> Callable[ + [gcd_document.UpdateDocumentRequest], Awaitable[operations.Operation] + ]: + r"""Return a callable for the update document method over gRPC. + + Updates the specified document. + + Operation + + Returns: + Callable[[~.UpdateDocumentRequest], + 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_document" not in self._stubs: + self._stubs["update_document"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/UpdateDocument", + request_serializer=gcd_document.UpdateDocumentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["update_document"] + + @property + def reload_document( + self, + ) -> Callable[[document.ReloadDocumentRequest], Awaitable[operations.Operation]]: + r"""Return a callable for the reload document method over gRPC. + + Reloads the specified document from its specified source, + content_uri or content. The previously loaded content of the + document will be deleted. Note: Even when the content of the + document has not changed, there still may be side effects + because of internal implementation changes. + + Note: The ``projects.agent.knowledgeBases.documents`` resource + is deprecated; only use ``projects.knowledgeBases.documents``. + + Operation + + Returns: + Callable[[~.ReloadDocumentRequest], + 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 "reload_document" not in self._stubs: + self._stubs["reload_document"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Documents/ReloadDocument", + request_serializer=document.ReloadDocumentRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["reload_document"] + + +__all__ = ("DocumentsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflow_v2/services/entity_types/async_client.py b/google/cloud/dialogflow_v2/services/entity_types/async_client.py index ce83f9dec..a3c67d500 100644 --- a/google/cloud/dialogflow_v2/services/entity_types/async_client.py +++ b/google/cloud/dialogflow_v2/services/entity_types/async_client.py @@ -79,7 +79,36 @@ class EntityTypesAsyncClient: EntityTypesClient.parse_common_location_path ) - from_service_account_file = EntityTypesClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EntityTypesAsyncClient: The constructed client. + """ + return EntityTypesClient.from_service_account_info.__func__(EntityTypesAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EntityTypesAsyncClient: The constructed client. + """ + return EntityTypesClient.from_service_account_file.__func__(EntityTypesAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -157,12 +186,13 @@ async def list_entity_types( agent. Args: - request (:class:`~.entity_type.ListEntityTypesRequest`): + request (:class:`google.cloud.dialogflow_v2.types.ListEntityTypesRequest`): The request object. The request message for [EntityTypes.ListEntityTypes][google.cloud.dialogflow.v2.EntityTypes.ListEntityTypes]. parent (:class:`str`): Required. The agent to list all entity types from. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -172,6 +202,7 @@ async def list_entity_types( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -183,7 +214,7 @@ async def list_entity_types( sent along with the request as metadata. Returns: - ~.pagers.ListEntityTypesAsyncPager: + google.cloud.dialogflow_v2.services.entity_types.pagers.ListEntityTypesAsyncPager: The response message for [EntityTypes.ListEntityTypes][google.cloud.dialogflow.v2.EntityTypes.ListEntityTypes]. @@ -250,12 +281,13 @@ async def get_entity_type( r"""Retrieves the specified entity type. Args: - request (:class:`~.entity_type.GetEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2.types.GetEntityTypeRequest`): The request object. The request message for [EntityTypes.GetEntityType][google.cloud.dialogflow.v2.EntityTypes.GetEntityType]. name (:class:`str`): Required. The name of the entity type. Format: ``projects//agent/entityTypes/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -265,6 +297,7 @@ async def get_entity_type( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -276,22 +309,22 @@ async def get_entity_type( sent along with the request as metadata. Returns: - ~.entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -348,16 +381,17 @@ async def create_entity_type( r"""Creates an entity type in the specified agent. Args: - request (:class:`~.gcd_entity_type.CreateEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2.types.CreateEntityTypeRequest`): The request object. The request message for [EntityTypes.CreateEntityType][google.cloud.dialogflow.v2.EntityTypes.CreateEntityType]. parent (:class:`str`): Required. The agent to create a entity type for. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entity_type (:class:`~.gcd_entity_type.EntityType`): + entity_type (:class:`google.cloud.dialogflow_v2.types.EntityType`): Required. The entity type to create. This corresponds to the ``entity_type`` field on the ``request`` instance; if ``request`` is provided, this @@ -368,6 +402,7 @@ async def create_entity_type( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -379,22 +414,22 @@ async def create_entity_type( sent along with the request as metadata. Returns: - ~.gcd_entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -452,10 +487,10 @@ async def update_entity_type( r"""Updates the specified entity type. Args: - request (:class:`~.gcd_entity_type.UpdateEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2.types.UpdateEntityTypeRequest`): The request object. The request message for [EntityTypes.UpdateEntityType][google.cloud.dialogflow.v2.EntityTypes.UpdateEntityType]. - entity_type (:class:`~.gcd_entity_type.EntityType`): + entity_type (:class:`google.cloud.dialogflow_v2.types.EntityType`): Required. The entity type to update. This corresponds to the ``entity_type`` field on the ``request`` instance; if ``request`` is provided, this @@ -466,6 +501,7 @@ async def update_entity_type( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -477,22 +513,22 @@ async def update_entity_type( sent along with the request as metadata. Returns: - ~.gcd_entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -549,12 +585,13 @@ async def delete_entity_type( r"""Deletes the specified entity type. Args: - request (:class:`~.entity_type.DeleteEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2.types.DeleteEntityTypeRequest`): The request object. The request message for [EntityTypes.DeleteEntityType][google.cloud.dialogflow.v2.EntityTypes.DeleteEntityType]. name (:class:`str`): Required. The name of the entity type to delete. Format: ``projects//agent/entityTypes/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -616,7 +653,7 @@ async def batch_update_entity_types( [BatchUpdateEntityTypesResponse][google.cloud.dialogflow.v2.BatchUpdateEntityTypesResponse]> Args: - request (:class:`~.entity_type.BatchUpdateEntityTypesRequest`): + request (:class:`google.cloud.dialogflow_v2.types.BatchUpdateEntityTypesRequest`): The request object. The request message for [EntityTypes.BatchUpdateEntityTypes][google.cloud.dialogflow.v2.EntityTypes.BatchUpdateEntityTypes]. @@ -627,11 +664,11 @@ async def batch_update_entity_types( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.entity_type.BatchUpdateEntityTypesResponse``: + :class:`google.cloud.dialogflow_v2.types.BatchUpdateEntityTypesResponse` The response message for [EntityTypes.BatchUpdateEntityTypes][google.cloud.dialogflow.v2.EntityTypes.BatchUpdateEntityTypes]. @@ -684,18 +721,20 @@ async def batch_delete_entity_types( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchDeleteEntityTypesRequest`): + request (:class:`google.cloud.dialogflow_v2.types.BatchDeleteEntityTypesRequest`): The request object. The request message for [EntityTypes.BatchDeleteEntityTypes][google.cloud.dialogflow.v2.EntityTypes.BatchDeleteEntityTypes]. parent (:class:`str`): Required. The name of the agent to delete all entities types for. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. entity_type_names (:class:`Sequence[str]`): Required. The names entity types to delete. All names must point to the same agent as ``parent``. + This corresponds to the ``entity_type_names`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -707,24 +746,22 @@ async def batch_delete_entity_types( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -793,17 +830,18 @@ async def batch_create_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchCreateEntitiesRequest`): + request (:class:`google.cloud.dialogflow_v2.types.BatchCreateEntitiesRequest`): The request object. The request message for [EntityTypes.BatchCreateEntities][google.cloud.dialogflow.v2.EntityTypes.BatchCreateEntities]. parent (:class:`str`): Required. The name of the entity type to create entities in. Format: ``projects//agent/entityTypes/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entities (:class:`Sequence[~.entity_type.EntityType.Entity]`): + entities (:class:`Sequence[google.cloud.dialogflow_v2.types.EntityType.Entity]`): Required. The entities to create. This corresponds to the ``entities`` field on the ``request`` instance; if ``request`` is provided, this @@ -814,6 +852,7 @@ async def batch_create_entities( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -825,24 +864,22 @@ async def batch_create_entities( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -915,19 +952,21 @@ async def batch_update_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchUpdateEntitiesRequest`): + request (:class:`google.cloud.dialogflow_v2.types.BatchUpdateEntitiesRequest`): The request object. The request message for [EntityTypes.BatchUpdateEntities][google.cloud.dialogflow.v2.EntityTypes.BatchUpdateEntities]. parent (:class:`str`): Required. The name of the entity type to update or create entities in. Format: ``projects//agent/entityTypes/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entities (:class:`Sequence[~.entity_type.EntityType.Entity]`): + entities (:class:`Sequence[google.cloud.dialogflow_v2.types.EntityType.Entity]`): Required. The entities to update or create. + This corresponds to the ``entities`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -937,6 +976,7 @@ async def batch_update_entities( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -948,24 +988,22 @@ async def batch_update_entities( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1036,13 +1074,14 @@ async def batch_delete_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchDeleteEntitiesRequest`): + request (:class:`google.cloud.dialogflow_v2.types.BatchDeleteEntitiesRequest`): The request object. The request message for [EntityTypes.BatchDeleteEntities][google.cloud.dialogflow.v2.EntityTypes.BatchDeleteEntities]. parent (:class:`str`): Required. The name of the entity type to delete entries for. Format: ``projects//agent/entityTypes/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1050,6 +1089,7 @@ async def batch_delete_entities( Required. The reference ``values`` of the entities to delete. Note that these are not fully-qualified names, i.e. they don't start with ``projects/``. + This corresponds to the ``entity_values`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1059,6 +1099,7 @@ async def batch_delete_entities( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1070,24 +1111,22 @@ async def batch_delete_entities( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. diff --git a/google/cloud/dialogflow_v2/services/entity_types/client.py b/google/cloud/dialogflow_v2/services/entity_types/client.py index f6e26a181..497f8a433 100644 --- a/google/cloud/dialogflow_v2/services/entity_types/client.py +++ b/google/cloud/dialogflow_v2/services/entity_types/client.py @@ -115,6 +115,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EntityTypesClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -127,7 +143,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + EntityTypesClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -234,10 +250,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.EntityTypesTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -273,21 +289,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -330,7 +342,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -349,21 +361,23 @@ def list_entity_types( agent. Args: - request (:class:`~.entity_type.ListEntityTypesRequest`): + request (google.cloud.dialogflow_v2.types.ListEntityTypesRequest): The request object. The request message for [EntityTypes.ListEntityTypes][google.cloud.dialogflow.v2.EntityTypes.ListEntityTypes]. - parent (:class:`str`): + parent (str): Required. The agent to list all entity types from. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -375,7 +389,7 @@ def list_entity_types( sent along with the request as metadata. Returns: - ~.pagers.ListEntityTypesPager: + google.cloud.dialogflow_v2.services.entity_types.pagers.ListEntityTypesPager: The response message for [EntityTypes.ListEntityTypes][google.cloud.dialogflow.v2.EntityTypes.ListEntityTypes]. @@ -443,21 +457,23 @@ def get_entity_type( r"""Retrieves the specified entity type. Args: - request (:class:`~.entity_type.GetEntityTypeRequest`): + request (google.cloud.dialogflow_v2.types.GetEntityTypeRequest): The request object. The request message for [EntityTypes.GetEntityType][google.cloud.dialogflow.v2.EntityTypes.GetEntityType]. - name (:class:`str`): + name (str): Required. The name of the entity type. Format: ``projects//agent/entityTypes/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -469,22 +485,22 @@ def get_entity_type( sent along with the request as metadata. Returns: - ~.entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -542,26 +558,28 @@ def create_entity_type( r"""Creates an entity type in the specified agent. Args: - request (:class:`~.gcd_entity_type.CreateEntityTypeRequest`): + request (google.cloud.dialogflow_v2.types.CreateEntityTypeRequest): The request object. The request message for [EntityTypes.CreateEntityType][google.cloud.dialogflow.v2.EntityTypes.CreateEntityType]. - parent (:class:`str`): + parent (str): Required. The agent to create a entity type for. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entity_type (:class:`~.gcd_entity_type.EntityType`): + entity_type (google.cloud.dialogflow_v2.types.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. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -573,22 +591,22 @@ def create_entity_type( sent along with the request as metadata. Returns: - ~.gcd_entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -647,20 +665,21 @@ def update_entity_type( r"""Updates the specified entity type. Args: - request (:class:`~.gcd_entity_type.UpdateEntityTypeRequest`): + request (google.cloud.dialogflow_v2.types.UpdateEntityTypeRequest): The request object. The request message for [EntityTypes.UpdateEntityType][google.cloud.dialogflow.v2.EntityTypes.UpdateEntityType]. - entity_type (:class:`~.gcd_entity_type.EntityType`): + entity_type (google.cloud.dialogflow_v2.types.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. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -672,22 +691,22 @@ def update_entity_type( sent along with the request as metadata. Returns: - ~.gcd_entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -745,12 +764,13 @@ def delete_entity_type( r"""Deletes the specified entity type. Args: - request (:class:`~.entity_type.DeleteEntityTypeRequest`): + request (google.cloud.dialogflow_v2.types.DeleteEntityTypeRequest): The request object. The request message for [EntityTypes.DeleteEntityType][google.cloud.dialogflow.v2.EntityTypes.DeleteEntityType]. - name (:class:`str`): + name (str): Required. The name of the entity type to delete. Format: ``projects//agent/entityTypes/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -813,7 +833,7 @@ def batch_update_entity_types( [BatchUpdateEntityTypesResponse][google.cloud.dialogflow.v2.BatchUpdateEntityTypesResponse]> Args: - request (:class:`~.entity_type.BatchUpdateEntityTypesRequest`): + request (google.cloud.dialogflow_v2.types.BatchUpdateEntityTypesRequest): The request object. The request message for [EntityTypes.BatchUpdateEntityTypes][google.cloud.dialogflow.v2.EntityTypes.BatchUpdateEntityTypes]. @@ -824,11 +844,11 @@ def batch_update_entity_types( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.entity_type.BatchUpdateEntityTypesResponse``: + :class:`google.cloud.dialogflow_v2.types.BatchUpdateEntityTypesResponse` The response message for [EntityTypes.BatchUpdateEntityTypes][google.cloud.dialogflow.v2.EntityTypes.BatchUpdateEntityTypes]. @@ -884,18 +904,20 @@ def batch_delete_entity_types( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchDeleteEntityTypesRequest`): + request (google.cloud.dialogflow_v2.types.BatchDeleteEntityTypesRequest): The request object. The request message for [EntityTypes.BatchDeleteEntityTypes][google.cloud.dialogflow.v2.EntityTypes.BatchDeleteEntityTypes]. - parent (:class:`str`): + parent (str): Required. The name of the agent to delete all entities types for. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entity_type_names (:class:`Sequence[str]`): + entity_type_names (Sequence[str]): Required. The names entity types to delete. All names must point to the same agent as ``parent``. + This corresponds to the ``entity_type_names`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -907,24 +929,22 @@ def batch_delete_entity_types( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -949,9 +969,8 @@ def batch_delete_entity_types( if parent is not None: request.parent = parent - - if entity_type_names: - request.entity_type_names.extend(entity_type_names) + if entity_type_names is not None: + request.entity_type_names = entity_type_names # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. @@ -996,27 +1015,29 @@ def batch_create_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchCreateEntitiesRequest`): + request (google.cloud.dialogflow_v2.types.BatchCreateEntitiesRequest): The request object. The request message for [EntityTypes.BatchCreateEntities][google.cloud.dialogflow.v2.EntityTypes.BatchCreateEntities]. - parent (:class:`str`): + parent (str): Required. The name of the entity type to create entities in. Format: ``projects//agent/entityTypes/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entities (:class:`Sequence[~.entity_type.EntityType.Entity]`): + entities (Sequence[google.cloud.dialogflow_v2.types.EntityType.Entity]): Required. The entities to create. This corresponds to the ``entities`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1028,24 +1049,22 @@ def batch_create_entities( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1070,12 +1089,11 @@ def batch_create_entities( if parent is not None: request.parent = parent + if entities is not None: + request.entities = entities if language_code is not None: request.language_code = language_code - if entities: - request.entities.extend(entities) - # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. rpc = self._transport._wrapped_methods[self._transport.batch_create_entities] @@ -1119,28 +1137,31 @@ def batch_update_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchUpdateEntitiesRequest`): + request (google.cloud.dialogflow_v2.types.BatchUpdateEntitiesRequest): The request object. The request message for [EntityTypes.BatchUpdateEntities][google.cloud.dialogflow.v2.EntityTypes.BatchUpdateEntities]. - parent (:class:`str`): + parent (str): Required. The name of the entity type to update or create entities in. Format: ``projects//agent/entityTypes/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entities (:class:`Sequence[~.entity_type.EntityType.Entity]`): + entities (Sequence[google.cloud.dialogflow_v2.types.EntityType.Entity]): Required. The entities to update or create. + This corresponds to the ``entities`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1152,24 +1173,22 @@ def batch_update_entities( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1194,12 +1213,11 @@ def batch_update_entities( if parent is not None: request.parent = parent + if entities is not None: + request.entities = entities if language_code is not None: request.language_code = language_code - if entities: - request.entities.extend(entities) - # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. rpc = self._transport._wrapped_methods[self._transport.batch_update_entities] @@ -1241,29 +1259,32 @@ def batch_delete_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchDeleteEntitiesRequest`): + request (google.cloud.dialogflow_v2.types.BatchDeleteEntitiesRequest): The request object. The request message for [EntityTypes.BatchDeleteEntities][google.cloud.dialogflow.v2.EntityTypes.BatchDeleteEntities]. - parent (:class:`str`): + parent (str): Required. The name of the entity type to delete entries for. Format: ``projects//agent/entityTypes/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entity_values (:class:`Sequence[str]`): + entity_values (Sequence[str]): Required. The reference ``values`` of the entities to delete. Note that these are not fully-qualified names, i.e. they don't start with ``projects/``. + This corresponds to the ``entity_values`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1275,24 +1296,22 @@ def batch_delete_entities( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1317,12 +1336,11 @@ def batch_delete_entities( if parent is not None: request.parent = parent + if entity_values is not None: + request.entity_values = entity_values if language_code is not None: request.language_code = language_code - if entity_values: - request.entity_values.extend(entity_values) - # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. rpc = self._transport._wrapped_methods[self._transport.batch_delete_entities] diff --git a/google/cloud/dialogflow_v2/services/entity_types/pagers.py b/google/cloud/dialogflow_v2/services/entity_types/pagers.py index b0c99ef5d..12d4aa9ed 100644 --- a/google/cloud/dialogflow_v2/services/entity_types/pagers.py +++ b/google/cloud/dialogflow_v2/services/entity_types/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2.types import entity_type @@ -24,7 +33,7 @@ class ListEntityTypesPager: """A pager for iterating through ``list_entity_types`` requests. This class thinly wraps an initial - :class:`~.entity_type.ListEntityTypesResponse` object, and + :class:`google.cloud.dialogflow_v2.types.ListEntityTypesResponse` object, and provides an ``__iter__`` method to iterate through its ``entity_types`` field. @@ -33,7 +42,7 @@ class ListEntityTypesPager: through the ``entity_types`` field on the corresponding responses. - All the usual :class:`~.entity_type.ListEntityTypesResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.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. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.entity_type.ListEntityTypesRequest`): + request (google.cloud.dialogflow_v2.types.ListEntityTypesRequest): The initial request object. - response (:class:`~.entity_type.ListEntityTypesResponse`): + response (google.cloud.dialogflow_v2.types.ListEntityTypesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListEntityTypesAsyncPager: """A pager for iterating through ``list_entity_types`` requests. This class thinly wraps an initial - :class:`~.entity_type.ListEntityTypesResponse` object, and + :class:`google.cloud.dialogflow_v2.types.ListEntityTypesResponse` object, and provides an ``__aiter__`` method to iterate through its ``entity_types`` field. @@ -95,7 +104,7 @@ class ListEntityTypesAsyncPager: through the ``entity_types`` field on the corresponding responses. - All the usual :class:`~.entity_type.ListEntityTypesResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.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. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.entity_type.ListEntityTypesRequest`): + request (google.cloud.dialogflow_v2.types.ListEntityTypesRequest): The initial request object. - response (:class:`~.entity_type.ListEntityTypesResponse`): + response (google.cloud.dialogflow_v2.types.ListEntityTypesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2/services/entity_types/transports/grpc.py b/google/cloud/dialogflow_v2/services/entity_types/transports/grpc.py index e4c3dee40..d8f6831af 100644 --- a/google/cloud/dialogflow_v2/services/entity_types/transports/grpc.py +++ b/google/cloud/dialogflow_v2/services/entity_types/transports/grpc.py @@ -62,6 +62,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -92,6 +93,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -108,6 +113,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -117,11 +127,6 @@ def __init__( 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 @@ -165,12 +170,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/entity_types/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/entity_types/transports/grpc_asyncio.py index 0c3c5cc50..ed7f16d05 100644 --- a/google/cloud/dialogflow_v2/services/entity_types/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2/services/entity_types/transports/grpc_asyncio.py @@ -106,6 +106,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -137,6 +138,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -153,6 +158,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -162,11 +172,6 @@ def __init__( 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 @@ -210,12 +215,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/environments/async_client.py b/google/cloud/dialogflow_v2/services/environments/async_client.py index 3cb2f6e24..acd916032 100644 --- a/google/cloud/dialogflow_v2/services/environments/async_client.py +++ b/google/cloud/dialogflow_v2/services/environments/async_client.py @@ -74,7 +74,36 @@ class EnvironmentsAsyncClient: EnvironmentsClient.parse_common_location_path ) - from_service_account_file = EnvironmentsClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EnvironmentsAsyncClient: The constructed client. + """ + return EnvironmentsClient.from_service_account_info.__func__(EnvironmentsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EnvironmentsAsyncClient: The constructed client. + """ + return EnvironmentsClient.from_service_account_file.__func__(EnvironmentsAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -150,7 +179,7 @@ async def list_environments( specified agent. Args: - request (:class:`~.environment.ListEnvironmentsRequest`): + request (:class:`google.cloud.dialogflow_v2.types.ListEnvironmentsRequest`): The request object. The request message for [Environments.ListEnvironments][google.cloud.dialogflow.v2.Environments.ListEnvironments]. @@ -161,7 +190,7 @@ async def list_environments( sent along with the request as metadata. Returns: - ~.pagers.ListEnvironmentsAsyncPager: + google.cloud.dialogflow_v2.services.environments.pagers.ListEnvironmentsAsyncPager: The response message for [Environments.ListEnvironments][google.cloud.dialogflow.v2.Environments.ListEnvironments]. diff --git a/google/cloud/dialogflow_v2/services/environments/client.py b/google/cloud/dialogflow_v2/services/environments/client.py index ce56d0729..d54531fe3 100644 --- a/google/cloud/dialogflow_v2/services/environments/client.py +++ b/google/cloud/dialogflow_v2/services/environments/client.py @@ -110,6 +110,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EnvironmentsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -122,7 +138,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + EnvironmentsClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -229,10 +245,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.EnvironmentsTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -268,21 +284,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -325,7 +337,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -342,7 +354,7 @@ def list_environments( specified agent. Args: - request (:class:`~.environment.ListEnvironmentsRequest`): + request (google.cloud.dialogflow_v2.types.ListEnvironmentsRequest): The request object. The request message for [Environments.ListEnvironments][google.cloud.dialogflow.v2.Environments.ListEnvironments]. @@ -353,7 +365,7 @@ def list_environments( sent along with the request as metadata. Returns: - ~.pagers.ListEnvironmentsPager: + google.cloud.dialogflow_v2.services.environments.pagers.ListEnvironmentsPager: The response message for [Environments.ListEnvironments][google.cloud.dialogflow.v2.Environments.ListEnvironments]. diff --git a/google/cloud/dialogflow_v2/services/environments/pagers.py b/google/cloud/dialogflow_v2/services/environments/pagers.py index 095287104..ea6aa5567 100644 --- a/google/cloud/dialogflow_v2/services/environments/pagers.py +++ b/google/cloud/dialogflow_v2/services/environments/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2.types import environment @@ -24,7 +33,7 @@ class ListEnvironmentsPager: """A pager for iterating through ``list_environments`` requests. This class thinly wraps an initial - :class:`~.environment.ListEnvironmentsResponse` object, and + :class:`google.cloud.dialogflow_v2.types.ListEnvironmentsResponse` object, and provides an ``__iter__`` method to iterate through its ``environments`` field. @@ -33,7 +42,7 @@ class ListEnvironmentsPager: through the ``environments`` field on the corresponding responses. - All the usual :class:`~.environment.ListEnvironmentsResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.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. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.environment.ListEnvironmentsRequest`): + request (google.cloud.dialogflow_v2.types.ListEnvironmentsRequest): The initial request object. - response (:class:`~.environment.ListEnvironmentsResponse`): + response (google.cloud.dialogflow_v2.types.ListEnvironmentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListEnvironmentsAsyncPager: """A pager for iterating through ``list_environments`` requests. This class thinly wraps an initial - :class:`~.environment.ListEnvironmentsResponse` object, and + :class:`google.cloud.dialogflow_v2.types.ListEnvironmentsResponse` object, and provides an ``__aiter__`` method to iterate through its ``environments`` field. @@ -95,7 +104,7 @@ class ListEnvironmentsAsyncPager: through the ``environments`` field on the corresponding responses. - All the usual :class:`~.environment.ListEnvironmentsResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.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. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.environment.ListEnvironmentsRequest`): + request (google.cloud.dialogflow_v2.types.ListEnvironmentsRequest): The initial request object. - response (:class:`~.environment.ListEnvironmentsResponse`): + response (google.cloud.dialogflow_v2.types.ListEnvironmentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2/services/environments/transports/grpc.py b/google/cloud/dialogflow_v2/services/environments/transports/grpc.py index d64912ac2..0106e187e 100644 --- a/google/cloud/dialogflow_v2/services/environments/transports/grpc.py +++ b/google/cloud/dialogflow_v2/services/environments/transports/grpc.py @@ -58,6 +58,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -88,6 +89,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -104,6 +109,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -113,11 +123,6 @@ def __init__( 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 @@ -161,12 +166,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/environments/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/environments/transports/grpc_asyncio.py index 837236fcf..25c848092 100644 --- a/google/cloud/dialogflow_v2/services/environments/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2/services/environments/transports/grpc_asyncio.py @@ -102,6 +102,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -133,6 +134,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -149,6 +154,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -158,11 +168,6 @@ def __init__( 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 @@ -206,12 +211,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/intents/async_client.py b/google/cloud/dialogflow_v2/services/intents/async_client.py index 992bd0222..2d1ba2aa9 100644 --- a/google/cloud/dialogflow_v2/services/intents/async_client.py +++ b/google/cloud/dialogflow_v2/services/intents/async_client.py @@ -77,7 +77,36 @@ class IntentsAsyncClient: 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 + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + IntentsAsyncClient: The constructed client. + """ + return IntentsClient.from_service_account_info.__func__(IntentsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + IntentsAsyncClient: The constructed client. + """ + return IntentsClient.from_service_account_file.__func__(IntentsAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -155,12 +184,13 @@ async def list_intents( agent. Args: - request (:class:`~.intent.ListIntentsRequest`): + request (:class:`google.cloud.dialogflow_v2.types.ListIntentsRequest`): The request object. The request message for [Intents.ListIntents][google.cloud.dialogflow.v2.Intents.ListIntents]. parent (:class:`str`): Required. The agent to list all intents from. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -170,6 +200,7 @@ async def list_intents( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -181,7 +212,7 @@ async def list_intents( sent along with the request as metadata. Returns: - ~.pagers.ListIntentsAsyncPager: + google.cloud.dialogflow_v2.services.intents.pagers.ListIntentsAsyncPager: The response message for [Intents.ListIntents][google.cloud.dialogflow.v2.Intents.ListIntents]. @@ -248,12 +279,13 @@ async def get_intent( r"""Retrieves the specified intent. Args: - request (:class:`~.intent.GetIntentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.GetIntentRequest`): The request object. The request message for [Intents.GetIntent][google.cloud.dialogflow.v2.Intents.GetIntent]. name (:class:`str`): Required. The name of the intent. Format: ``projects//agent/intents/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -263,6 +295,7 @@ async def get_intent( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -274,18 +307,18 @@ async def get_intent( sent along with the request as metadata. Returns: - ~.intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -342,16 +375,17 @@ async def create_intent( r"""Creates an intent in the specified agent. Args: - request (:class:`~.gcd_intent.CreateIntentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.CreateIntentRequest`): The request object. The request message for [Intents.CreateIntent][google.cloud.dialogflow.v2.Intents.CreateIntent]. parent (:class:`str`): Required. The agent to create a intent for. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intent (:class:`~.gcd_intent.Intent`): + intent (:class:`google.cloud.dialogflow_v2.types.Intent`): Required. The intent to create. This corresponds to the ``intent`` field on the ``request`` instance; if ``request`` is provided, this @@ -362,6 +396,7 @@ async def create_intent( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -373,18 +408,18 @@ async def create_intent( sent along with the request as metadata. Returns: - ~.gcd_intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -443,10 +478,10 @@ async def update_intent( r"""Updates the specified intent. Args: - request (:class:`~.gcd_intent.UpdateIntentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.UpdateIntentRequest`): The request object. The request message for [Intents.UpdateIntent][google.cloud.dialogflow.v2.Intents.UpdateIntent]. - intent (:class:`~.gcd_intent.Intent`): + intent (:class:`google.cloud.dialogflow_v2.types.Intent`): Required. The intent to update. This corresponds to the ``intent`` field on the ``request`` instance; if ``request`` is provided, this @@ -457,12 +492,14 @@ async def update_intent( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): Optional. 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. @@ -474,18 +511,18 @@ async def update_intent( sent along with the request as metadata. Returns: - ~.gcd_intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -545,7 +582,7 @@ async def delete_intent( indirect followup intents. Args: - request (:class:`~.intent.DeleteIntentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.DeleteIntentRequest`): The request object. The request message for [Intents.DeleteIntent][google.cloud.dialogflow.v2.Intents.DeleteIntent]. name (:class:`str`): @@ -553,6 +590,7 @@ async def delete_intent( intent has direct or indirect followup intents, we also delete them. Format: ``projects//agent/intents/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -617,11 +655,12 @@ async def batch_update_intents( [BatchUpdateIntentsResponse][google.cloud.dialogflow.v2.BatchUpdateIntentsResponse]> Args: - request (:class:`~.intent.BatchUpdateIntentsRequest`): + request (:class:`google.cloud.dialogflow_v2.types.BatchUpdateIntentsRequest`): The request object. parent (:class:`str`): Required. The name of the agent to update or create intents in. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -632,12 +671,14 @@ async def batch_update_intents( serialized proto (of IntentBatch type) or JSON object. Note: The URI must start with "gs://". + This corresponds to the ``intent_batch_uri`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intent_batch_inline (:class:`~.intent.IntentBatch`): + intent_batch_inline (:class:`google.cloud.dialogflow_v2.types.IntentBatch`): The collection of intents to update or create. + This corresponds to the ``intent_batch_inline`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -649,12 +690,12 @@ async def batch_update_intents( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.intent.BatchUpdateIntentsResponse``: The - response message for + :class:`google.cloud.dialogflow_v2.types.BatchUpdateIntentsResponse` + The response message for [Intents.BatchUpdateIntents][google.cloud.dialogflow.v2.Intents.BatchUpdateIntents]. """ @@ -724,18 +765,20 @@ async def batch_delete_intents( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.intent.BatchDeleteIntentsRequest`): + request (:class:`google.cloud.dialogflow_v2.types.BatchDeleteIntentsRequest`): The request object. The request message for [Intents.BatchDeleteIntents][google.cloud.dialogflow.v2.Intents.BatchDeleteIntents]. parent (:class:`str`): Required. The name of the agent to delete all entities types for. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intents (:class:`Sequence[~.intent.Intent]`): + intents (:class:`Sequence[google.cloud.dialogflow_v2.types.Intent]`): Required. The collection of intents to delete. Only intent ``name`` must be filled in. + This corresponds to the ``intents`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -747,24 +790,22 @@ async def batch_delete_intents( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. diff --git a/google/cloud/dialogflow_v2/services/intents/client.py b/google/cloud/dialogflow_v2/services/intents/client.py index d6d982579..fd249f7fc 100644 --- a/google/cloud/dialogflow_v2/services/intents/client.py +++ b/google/cloud/dialogflow_v2/services/intents/client.py @@ -115,6 +115,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + IntentsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -127,7 +143,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + IntentsClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -248,10 +264,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.IntentsTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -287,21 +303,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -344,7 +356,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -363,21 +375,23 @@ def list_intents( agent. Args: - request (:class:`~.intent.ListIntentsRequest`): + request (google.cloud.dialogflow_v2.types.ListIntentsRequest): The request object. The request message for [Intents.ListIntents][google.cloud.dialogflow.v2.Intents.ListIntents]. - parent (:class:`str`): + parent (str): Required. The agent to list all intents from. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -389,7 +403,7 @@ def list_intents( sent along with the request as metadata. Returns: - ~.pagers.ListIntentsPager: + google.cloud.dialogflow_v2.services.intents.pagers.ListIntentsPager: The response message for [Intents.ListIntents][google.cloud.dialogflow.v2.Intents.ListIntents]. @@ -457,21 +471,23 @@ def get_intent( r"""Retrieves the specified intent. Args: - request (:class:`~.intent.GetIntentRequest`): + request (google.cloud.dialogflow_v2.types.GetIntentRequest): The request object. The request message for [Intents.GetIntent][google.cloud.dialogflow.v2.Intents.GetIntent]. - name (:class:`str`): + name (str): Required. The name of the intent. Format: ``projects//agent/intents/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -483,18 +499,18 @@ def get_intent( sent along with the request as metadata. Returns: - ~.intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -552,26 +568,28 @@ def create_intent( r"""Creates an intent in the specified agent. Args: - request (:class:`~.gcd_intent.CreateIntentRequest`): + request (google.cloud.dialogflow_v2.types.CreateIntentRequest): The request object. The request message for [Intents.CreateIntent][google.cloud.dialogflow.v2.Intents.CreateIntent]. - parent (:class:`str`): + parent (str): Required. The agent to create a intent for. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intent (:class:`~.gcd_intent.Intent`): + intent (google.cloud.dialogflow_v2.types.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. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -583,18 +601,18 @@ def create_intent( sent along with the request as metadata. Returns: - ~.gcd_intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -654,26 +672,28 @@ def update_intent( r"""Updates the specified intent. Args: - request (:class:`~.gcd_intent.UpdateIntentRequest`): + request (google.cloud.dialogflow_v2.types.UpdateIntentRequest): The request object. The request message for [Intents.UpdateIntent][google.cloud.dialogflow.v2.Intents.UpdateIntent]. - intent (:class:`~.gcd_intent.Intent`): + intent (google.cloud.dialogflow_v2.types.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. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. 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. @@ -685,18 +705,18 @@ def update_intent( sent along with the request as metadata. Returns: - ~.gcd_intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -757,14 +777,15 @@ def delete_intent( indirect followup intents. Args: - request (:class:`~.intent.DeleteIntentRequest`): + request (google.cloud.dialogflow_v2.types.DeleteIntentRequest): The request object. The request message for [Intents.DeleteIntent][google.cloud.dialogflow.v2.Intents.DeleteIntent]. - name (:class:`str`): + name (str): Required. The name of the intent to delete. If this intent has direct or indirect followup intents, we also delete them. Format: ``projects//agent/intents/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -830,27 +851,30 @@ def batch_update_intents( [BatchUpdateIntentsResponse][google.cloud.dialogflow.v2.BatchUpdateIntentsResponse]> Args: - request (:class:`~.intent.BatchUpdateIntentsRequest`): + request (google.cloud.dialogflow_v2.types.BatchUpdateIntentsRequest): The request object. - parent (:class:`str`): + parent (str): Required. The name of the agent to update or create intents in. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intent_batch_uri (:class:`str`): + intent_batch_uri (str): The URI to a Google Cloud Storage file containing intents to update or create. The file format can either be a serialized proto (of IntentBatch type) or JSON object. Note: The URI must start with "gs://". + This corresponds to the ``intent_batch_uri`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intent_batch_inline (:class:`~.intent.IntentBatch`): + intent_batch_inline (google.cloud.dialogflow_v2.types.IntentBatch): The collection of intents to update or create. + This corresponds to the ``intent_batch_inline`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -862,12 +886,12 @@ def batch_update_intents( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.intent.BatchUpdateIntentsResponse``: The - response message for + :class:`google.cloud.dialogflow_v2.types.BatchUpdateIntentsResponse` + The response message for [Intents.BatchUpdateIntents][google.cloud.dialogflow.v2.Intents.BatchUpdateIntents]. """ @@ -938,18 +962,20 @@ def batch_delete_intents( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.intent.BatchDeleteIntentsRequest`): + request (google.cloud.dialogflow_v2.types.BatchDeleteIntentsRequest): The request object. The request message for [Intents.BatchDeleteIntents][google.cloud.dialogflow.v2.Intents.BatchDeleteIntents]. - parent (:class:`str`): + parent (str): Required. The name of the agent to delete all entities types for. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intents (:class:`Sequence[~.intent.Intent]`): + intents (Sequence[google.cloud.dialogflow_v2.types.Intent]): Required. The collection of intents to delete. Only intent ``name`` must be filled in. + This corresponds to the ``intents`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -961,24 +987,22 @@ def batch_delete_intents( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1003,9 +1027,8 @@ def batch_delete_intents( if parent is not None: request.parent = parent - - if intents: - request.intents.extend(intents) + if intents is not None: + request.intents = intents # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. diff --git a/google/cloud/dialogflow_v2/services/intents/pagers.py b/google/cloud/dialogflow_v2/services/intents/pagers.py index ddbfb28f7..d19ace9a4 100644 --- a/google/cloud/dialogflow_v2/services/intents/pagers.py +++ b/google/cloud/dialogflow_v2/services/intents/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2.types import intent @@ -24,7 +33,7 @@ class ListIntentsPager: """A pager for iterating through ``list_intents`` requests. This class thinly wraps an initial - :class:`~.intent.ListIntentsResponse` object, and + :class:`google.cloud.dialogflow_v2.types.ListIntentsResponse` object, and provides an ``__iter__`` method to iterate through its ``intents`` field. @@ -33,7 +42,7 @@ class ListIntentsPager: through the ``intents`` field on the corresponding responses. - All the usual :class:`~.intent.ListIntentsResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.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. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.intent.ListIntentsRequest`): + request (google.cloud.dialogflow_v2.types.ListIntentsRequest): The initial request object. - response (:class:`~.intent.ListIntentsResponse`): + response (google.cloud.dialogflow_v2.types.ListIntentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListIntentsAsyncPager: """A pager for iterating through ``list_intents`` requests. This class thinly wraps an initial - :class:`~.intent.ListIntentsResponse` object, and + :class:`google.cloud.dialogflow_v2.types.ListIntentsResponse` object, and provides an ``__aiter__`` method to iterate through its ``intents`` field. @@ -95,7 +104,7 @@ class ListIntentsAsyncPager: through the ``intents`` field on the corresponding responses. - All the usual :class:`~.intent.ListIntentsResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.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. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.intent.ListIntentsRequest`): + request (google.cloud.dialogflow_v2.types.ListIntentsRequest): The initial request object. - response (:class:`~.intent.ListIntentsResponse`): + response (google.cloud.dialogflow_v2.types.ListIntentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2/services/intents/transports/grpc.py b/google/cloud/dialogflow_v2/services/intents/transports/grpc.py index d82b2f28e..eda6678ef 100644 --- a/google/cloud/dialogflow_v2/services/intents/transports/grpc.py +++ b/google/cloud/dialogflow_v2/services/intents/transports/grpc.py @@ -61,6 +61,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -91,6 +92,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -107,6 +112,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -116,11 +126,6 @@ def __init__( 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 @@ -164,12 +169,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/intents/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/intents/transports/grpc_asyncio.py index 86ac4cfef..90a158dc9 100644 --- a/google/cloud/dialogflow_v2/services/intents/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2/services/intents/transports/grpc_asyncio.py @@ -105,6 +105,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -136,6 +137,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -152,6 +157,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -161,11 +171,6 @@ def __init__( 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 @@ -209,12 +214,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/knowledge_bases/__init__.py b/google/cloud/dialogflow_v2/services/knowledge_bases/__init__.py new file mode 100644 index 000000000..be24a37d1 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/knowledge_bases/__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 KnowledgeBasesClient +from .async_client import KnowledgeBasesAsyncClient + +__all__ = ( + "KnowledgeBasesClient", + "KnowledgeBasesAsyncClient", +) diff --git a/google/cloud/dialogflow_v2/services/knowledge_bases/async_client.py b/google/cloud/dialogflow_v2/services/knowledge_bases/async_client.py new file mode 100644 index 000000000..33d074e30 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/knowledge_bases/async_client.py @@ -0,0 +1,614 @@ +# -*- 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.dialogflow_v2.services.knowledge_bases import pagers +from google.cloud.dialogflow_v2.types import knowledge_base +from google.cloud.dialogflow_v2.types import knowledge_base as gcd_knowledge_base +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import KnowledgeBasesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import KnowledgeBasesGrpcAsyncIOTransport +from .client import KnowledgeBasesClient + + +class KnowledgeBasesAsyncClient: + """Service for managing + [KnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBase]. + """ + + _client: KnowledgeBasesClient + + DEFAULT_ENDPOINT = KnowledgeBasesClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = KnowledgeBasesClient.DEFAULT_MTLS_ENDPOINT + + knowledge_base_path = staticmethod(KnowledgeBasesClient.knowledge_base_path) + parse_knowledge_base_path = staticmethod( + KnowledgeBasesClient.parse_knowledge_base_path + ) + + common_billing_account_path = staticmethod( + KnowledgeBasesClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + KnowledgeBasesClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(KnowledgeBasesClient.common_folder_path) + parse_common_folder_path = staticmethod( + KnowledgeBasesClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + KnowledgeBasesClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + KnowledgeBasesClient.parse_common_organization_path + ) + + common_project_path = staticmethod(KnowledgeBasesClient.common_project_path) + parse_common_project_path = staticmethod( + KnowledgeBasesClient.parse_common_project_path + ) + + common_location_path = staticmethod(KnowledgeBasesClient.common_location_path) + parse_common_location_path = staticmethod( + KnowledgeBasesClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + KnowledgeBasesAsyncClient: The constructed client. + """ + return KnowledgeBasesClient.from_service_account_info.__func__(KnowledgeBasesAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + KnowledgeBasesAsyncClient: The constructed client. + """ + return KnowledgeBasesClient.from_service_account_file.__func__(KnowledgeBasesAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> KnowledgeBasesTransport: + """Return the transport used by the client instance. + + Returns: + KnowledgeBasesTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(KnowledgeBasesClient).get_transport_class, type(KnowledgeBasesClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, KnowledgeBasesTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the knowledge bases 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, ~.KnowledgeBasesTransport]): 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 = KnowledgeBasesClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_knowledge_bases( + self, + request: knowledge_base.ListKnowledgeBasesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListKnowledgeBasesAsyncPager: + r"""Returns the list of all knowledge bases of the + specified agent. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.ListKnowledgeBasesRequest`): + The request object. Request message for + [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBases.ListKnowledgeBases]. + parent (:class:`str`): + Required. The project to list of knowledge bases 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: + google.cloud.dialogflow_v2.services.knowledge_bases.pagers.ListKnowledgeBasesAsyncPager: + Response message for + [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBases.ListKnowledgeBases]. + + 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 = knowledge_base.ListKnowledgeBasesRequest(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_knowledge_bases, + 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.ListKnowledgeBasesAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_knowledge_base( + self, + request: knowledge_base.GetKnowledgeBaseRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> knowledge_base.KnowledgeBase: + r"""Retrieves the specified knowledge base. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.GetKnowledgeBaseRequest`): + The request object. Request message for + [KnowledgeBases.GetKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.GetKnowledgeBase]. + name (:class:`str`): + Required. The name of the knowledge base to retrieve. + Format + ``projects//locations//knowledgeBases/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. + + """ + # Create or coerce a 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 = knowledge_base.GetKnowledgeBaseRequest(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_knowledge_base, + 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_knowledge_base( + self, + request: gcd_knowledge_base.CreateKnowledgeBaseRequest = None, + *, + parent: str = None, + knowledge_base: gcd_knowledge_base.KnowledgeBase = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_knowledge_base.KnowledgeBase: + r"""Creates a knowledge base. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.CreateKnowledgeBaseRequest`): + The request object. Request message for + [KnowledgeBases.CreateKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.CreateKnowledgeBase]. + parent (:class:`str`): + Required. The project to create a knowledge base for. + Format: + ``projects//locations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + knowledge_base (:class:`google.cloud.dialogflow_v2.types.KnowledgeBase`): + Required. The knowledge base to + create. + + This corresponds to the ``knowledge_base`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. + + """ + # Create or coerce a 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, knowledge_base]) + if request is 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 = gcd_knowledge_base.CreateKnowledgeBaseRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if knowledge_base is not None: + request.knowledge_base = knowledge_base + + # 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_knowledge_base, + 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_knowledge_base( + self, + request: knowledge_base.DeleteKnowledgeBaseRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified knowledge base. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.DeleteKnowledgeBaseRequest`): + The request object. Request message for + [KnowledgeBases.DeleteKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.DeleteKnowledgeBase]. + name (:class:`str`): + Required. The name of the knowledge base to delete. + Format: + ``projects//locations//knowledgeBases/``. + + 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 = knowledge_base.DeleteKnowledgeBaseRequest(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_knowledge_base, + 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 update_knowledge_base( + self, + request: gcd_knowledge_base.UpdateKnowledgeBaseRequest = None, + *, + knowledge_base: gcd_knowledge_base.KnowledgeBase = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_knowledge_base.KnowledgeBase: + r"""Updates the specified knowledge base. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.UpdateKnowledgeBaseRequest`): + The request object. Request message for + [KnowledgeBases.UpdateKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.UpdateKnowledgeBase]. + knowledge_base (:class:`google.cloud.dialogflow_v2.types.KnowledgeBase`): + Required. The knowledge base to + update. + + This corresponds to the ``knowledge_base`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): + Optional. Not specified means ``update all``. Currently, + only ``display_name`` can be updated, an InvalidArgument + will be returned for attempting to update other fields. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. + + """ + # Create or coerce a 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([knowledge_base, 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 = gcd_knowledge_base.UpdateKnowledgeBaseRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if knowledge_base is not None: + request.knowledge_base = knowledge_base + 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_knowledge_base, + 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( + (("knowledge_base.name", request.knowledge_base.name),) + ), + ) + + # 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("KnowledgeBasesAsyncClient",) diff --git a/google/cloud/dialogflow_v2/services/knowledge_bases/client.py b/google/cloud/dialogflow_v2/services/knowledge_bases/client.py new file mode 100644 index 000000000..d5f171ef9 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/knowledge_bases/client.py @@ -0,0 +1,790 @@ +# -*- 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.dialogflow_v2.services.knowledge_bases import pagers +from google.cloud.dialogflow_v2.types import knowledge_base +from google.cloud.dialogflow_v2.types import knowledge_base as gcd_knowledge_base +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import KnowledgeBasesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import KnowledgeBasesGrpcTransport +from .transports.grpc_asyncio import KnowledgeBasesGrpcAsyncIOTransport + + +class KnowledgeBasesClientMeta(type): + """Metaclass for the KnowledgeBases 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[KnowledgeBasesTransport]] + _transport_registry["grpc"] = KnowledgeBasesGrpcTransport + _transport_registry["grpc_asyncio"] = KnowledgeBasesGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[KnowledgeBasesTransport]: + """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 KnowledgeBasesClient(metaclass=KnowledgeBasesClientMeta): + """Service for managing + [KnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBase]. + """ + + @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_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + KnowledgeBasesClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + KnowledgeBasesClient: 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) -> KnowledgeBasesTransport: + """Return the transport used by the client instance. + + Returns: + KnowledgeBasesTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def knowledge_base_path(project: str, knowledge_base: str,) -> str: + """Return a fully-qualified knowledge_base string.""" + return "projects/{project}/knowledgeBases/{knowledge_base}".format( + project=project, knowledge_base=knowledge_base, + ) + + @staticmethod + def parse_knowledge_base_path(path: str) -> Dict[str, str]: + """Parse a knowledge_base path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/knowledgeBases/(?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, KnowledgeBasesTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the knowledge bases 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, KnowledgeBasesTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, KnowledgeBasesTransport): + # transport is a KnowledgeBasesTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_knowledge_bases( + self, + request: knowledge_base.ListKnowledgeBasesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListKnowledgeBasesPager: + r"""Returns the list of all knowledge bases of the + specified agent. + + Args: + request (google.cloud.dialogflow_v2.types.ListKnowledgeBasesRequest): + The request object. Request message for + [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBases.ListKnowledgeBases]. + parent (str): + Required. The project to list of knowledge bases 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: + google.cloud.dialogflow_v2.services.knowledge_bases.pagers.ListKnowledgeBasesPager: + Response message for + [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBases.ListKnowledgeBases]. + + 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 knowledge_base.ListKnowledgeBasesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, knowledge_base.ListKnowledgeBasesRequest): + request = knowledge_base.ListKnowledgeBasesRequest(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_knowledge_bases] + + # 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.ListKnowledgeBasesPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_knowledge_base( + self, + request: knowledge_base.GetKnowledgeBaseRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> knowledge_base.KnowledgeBase: + r"""Retrieves the specified knowledge base. + + Args: + request (google.cloud.dialogflow_v2.types.GetKnowledgeBaseRequest): + The request object. Request message for + [KnowledgeBases.GetKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.GetKnowledgeBase]. + name (str): + Required. The name of the knowledge base to retrieve. + Format + ``projects//locations//knowledgeBases/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. + + """ + # Create or coerce a 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 knowledge_base.GetKnowledgeBaseRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, knowledge_base.GetKnowledgeBaseRequest): + request = knowledge_base.GetKnowledgeBaseRequest(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_knowledge_base] + + # 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_knowledge_base( + self, + request: gcd_knowledge_base.CreateKnowledgeBaseRequest = None, + *, + parent: str = None, + knowledge_base: gcd_knowledge_base.KnowledgeBase = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_knowledge_base.KnowledgeBase: + r"""Creates a knowledge base. + + Args: + request (google.cloud.dialogflow_v2.types.CreateKnowledgeBaseRequest): + The request object. Request message for + [KnowledgeBases.CreateKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.CreateKnowledgeBase]. + parent (str): + Required. The project to create a knowledge base for. + Format: + ``projects//locations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + knowledge_base (google.cloud.dialogflow_v2.types.KnowledgeBase): + Required. The knowledge base to + create. + + This corresponds to the ``knowledge_base`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. + + """ + # Create or coerce a 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, knowledge_base]) + if request is 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 gcd_knowledge_base.CreateKnowledgeBaseRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_knowledge_base.CreateKnowledgeBaseRequest): + request = gcd_knowledge_base.CreateKnowledgeBaseRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if knowledge_base is not None: + request.knowledge_base = knowledge_base + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_knowledge_base] + + # 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_knowledge_base( + self, + request: knowledge_base.DeleteKnowledgeBaseRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified knowledge base. + + Args: + request (google.cloud.dialogflow_v2.types.DeleteKnowledgeBaseRequest): + The request object. Request message for + [KnowledgeBases.DeleteKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.DeleteKnowledgeBase]. + name (str): + Required. The name of the knowledge base to delete. + Format: + ``projects//locations//knowledgeBases/``. + + 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 knowledge_base.DeleteKnowledgeBaseRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, knowledge_base.DeleteKnowledgeBaseRequest): + request = knowledge_base.DeleteKnowledgeBaseRequest(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_knowledge_base] + + # 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 update_knowledge_base( + self, + request: gcd_knowledge_base.UpdateKnowledgeBaseRequest = None, + *, + knowledge_base: gcd_knowledge_base.KnowledgeBase = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_knowledge_base.KnowledgeBase: + r"""Updates the specified knowledge base. + + Args: + request (google.cloud.dialogflow_v2.types.UpdateKnowledgeBaseRequest): + The request object. Request message for + [KnowledgeBases.UpdateKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.UpdateKnowledgeBase]. + knowledge_base (google.cloud.dialogflow_v2.types.KnowledgeBase): + Required. The knowledge base to + update. + + This corresponds to the ``knowledge_base`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Optional. Not specified means ``update all``. Currently, + only ``display_name`` can be updated, an InvalidArgument + will be returned for attempting to update other fields. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. + + """ + # Create or coerce a 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([knowledge_base, 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 gcd_knowledge_base.UpdateKnowledgeBaseRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_knowledge_base.UpdateKnowledgeBaseRequest): + request = gcd_knowledge_base.UpdateKnowledgeBaseRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if knowledge_base is not None: + request.knowledge_base = knowledge_base + 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_knowledge_base] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("knowledge_base.name", request.knowledge_base.name),) + ), + ) + + # 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("KnowledgeBasesClient",) diff --git a/google/cloud/dialogflow_v2/services/knowledge_bases/pagers.py b/google/cloud/dialogflow_v2/services/knowledge_bases/pagers.py new file mode 100644 index 000000000..6939025df --- /dev/null +++ b/google/cloud/dialogflow_v2/services/knowledge_bases/pagers.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.dialogflow_v2.types import knowledge_base + + +class ListKnowledgeBasesPager: + """A pager for iterating through ``list_knowledge_bases`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListKnowledgeBasesResponse` object, and + provides an ``__iter__`` method to iterate through its + ``knowledge_bases`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListKnowledgeBases`` requests and continue to iterate + through the ``knowledge_bases`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListKnowledgeBasesResponse` + 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[..., knowledge_base.ListKnowledgeBasesResponse], + request: knowledge_base.ListKnowledgeBasesRequest, + response: knowledge_base.ListKnowledgeBasesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListKnowledgeBasesRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListKnowledgeBasesResponse): + 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 = knowledge_base.ListKnowledgeBasesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[knowledge_base.ListKnowledgeBasesResponse]: + 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[knowledge_base.KnowledgeBase]: + for page in self.pages: + yield from page.knowledge_bases + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListKnowledgeBasesAsyncPager: + """A pager for iterating through ``list_knowledge_bases`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListKnowledgeBasesResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``knowledge_bases`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListKnowledgeBases`` requests and continue to iterate + through the ``knowledge_bases`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListKnowledgeBasesResponse` + 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[knowledge_base.ListKnowledgeBasesResponse]], + request: knowledge_base.ListKnowledgeBasesRequest, + response: knowledge_base.ListKnowledgeBasesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListKnowledgeBasesRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListKnowledgeBasesResponse): + 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 = knowledge_base.ListKnowledgeBasesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[knowledge_base.ListKnowledgeBasesResponse]: + 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[knowledge_base.KnowledgeBase]: + async def async_generator(): + async for page in self.pages: + for response in page.knowledge_bases: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflow_v2/services/knowledge_bases/transports/__init__.py b/google/cloud/dialogflow_v2/services/knowledge_bases/transports/__init__.py new file mode 100644 index 000000000..a3ac400b0 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/knowledge_bases/transports/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import KnowledgeBasesTransport +from .grpc import KnowledgeBasesGrpcTransport +from .grpc_asyncio import KnowledgeBasesGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[KnowledgeBasesTransport]] +_transport_registry["grpc"] = KnowledgeBasesGrpcTransport +_transport_registry["grpc_asyncio"] = KnowledgeBasesGrpcAsyncIOTransport + +__all__ = ( + "KnowledgeBasesTransport", + "KnowledgeBasesGrpcTransport", + "KnowledgeBasesGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflow_v2/services/knowledge_bases/transports/base.py b/google/cloud/dialogflow_v2/services/knowledge_bases/transports/base.py new file mode 100644 index 000000000..9e68503ba --- /dev/null +++ b/google/cloud/dialogflow_v2/services/knowledge_bases/transports/base.py @@ -0,0 +1,196 @@ +# -*- 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.dialogflow_v2.types import knowledge_base +from google.cloud.dialogflow_v2.types import knowledge_base as gcd_knowledge_base +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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class KnowledgeBasesTransport(abc.ABC): + """Abstract transport class for KnowledgeBases.""" + + 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_knowledge_bases: gapic_v1.method.wrap_method( + self.list_knowledge_bases, + default_timeout=None, + client_info=client_info, + ), + self.get_knowledge_base: gapic_v1.method.wrap_method( + self.get_knowledge_base, default_timeout=None, client_info=client_info, + ), + self.create_knowledge_base: gapic_v1.method.wrap_method( + self.create_knowledge_base, + default_timeout=None, + client_info=client_info, + ), + self.delete_knowledge_base: gapic_v1.method.wrap_method( + self.delete_knowledge_base, + default_timeout=None, + client_info=client_info, + ), + self.update_knowledge_base: gapic_v1.method.wrap_method( + self.update_knowledge_base, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def list_knowledge_bases( + self, + ) -> typing.Callable[ + [knowledge_base.ListKnowledgeBasesRequest], + typing.Union[ + knowledge_base.ListKnowledgeBasesResponse, + typing.Awaitable[knowledge_base.ListKnowledgeBasesResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_knowledge_base( + self, + ) -> typing.Callable[ + [knowledge_base.GetKnowledgeBaseRequest], + typing.Union[ + knowledge_base.KnowledgeBase, typing.Awaitable[knowledge_base.KnowledgeBase] + ], + ]: + raise NotImplementedError() + + @property + def create_knowledge_base( + self, + ) -> typing.Callable[ + [gcd_knowledge_base.CreateKnowledgeBaseRequest], + typing.Union[ + gcd_knowledge_base.KnowledgeBase, + typing.Awaitable[gcd_knowledge_base.KnowledgeBase], + ], + ]: + raise NotImplementedError() + + @property + def delete_knowledge_base( + self, + ) -> typing.Callable[ + [knowledge_base.DeleteKnowledgeBaseRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + @property + def update_knowledge_base( + self, + ) -> typing.Callable[ + [gcd_knowledge_base.UpdateKnowledgeBaseRequest], + typing.Union[ + gcd_knowledge_base.KnowledgeBase, + typing.Awaitable[gcd_knowledge_base.KnowledgeBase], + ], + ]: + raise NotImplementedError() + + +__all__ = ("KnowledgeBasesTransport",) diff --git a/google/cloud/dialogflow_v2/services/knowledge_bases/transports/grpc.py b/google/cloud/dialogflow_v2/services/knowledge_bases/transports/grpc.py new file mode 100644 index 000000000..44ec2542a --- /dev/null +++ b/google/cloud/dialogflow_v2/services/knowledge_bases/transports/grpc.py @@ -0,0 +1,397 @@ +# -*- 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.dialogflow_v2.types import knowledge_base +from google.cloud.dialogflow_v2.types import knowledge_base as gcd_knowledge_base +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import KnowledgeBasesTransport, DEFAULT_CLIENT_INFO + + +class KnowledgeBasesGrpcTransport(KnowledgeBasesTransport): + """gRPC backend transport for KnowledgeBases. + + Service for managing + [KnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBase]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + self._stubs = {} # type: Dict[str, Callable] + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + @classmethod + def create_channel( + cls, + host: str = "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 (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def list_knowledge_bases( + self, + ) -> Callable[ + [knowledge_base.ListKnowledgeBasesRequest], + knowledge_base.ListKnowledgeBasesResponse, + ]: + r"""Return a callable for the list knowledge bases method over gRPC. + + Returns the list of all knowledge bases of the + specified agent. + + Returns: + Callable[[~.ListKnowledgeBasesRequest], + ~.ListKnowledgeBasesResponse]: + 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_knowledge_bases" not in self._stubs: + self._stubs["list_knowledge_bases"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.KnowledgeBases/ListKnowledgeBases", + request_serializer=knowledge_base.ListKnowledgeBasesRequest.serialize, + response_deserializer=knowledge_base.ListKnowledgeBasesResponse.deserialize, + ) + return self._stubs["list_knowledge_bases"] + + @property + def get_knowledge_base( + self, + ) -> Callable[ + [knowledge_base.GetKnowledgeBaseRequest], knowledge_base.KnowledgeBase + ]: + r"""Return a callable for the get knowledge base method over gRPC. + + Retrieves the specified knowledge base. + + Returns: + Callable[[~.GetKnowledgeBaseRequest], + ~.KnowledgeBase]: + 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_knowledge_base" not in self._stubs: + self._stubs["get_knowledge_base"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.KnowledgeBases/GetKnowledgeBase", + request_serializer=knowledge_base.GetKnowledgeBaseRequest.serialize, + response_deserializer=knowledge_base.KnowledgeBase.deserialize, + ) + return self._stubs["get_knowledge_base"] + + @property + def create_knowledge_base( + self, + ) -> Callable[ + [gcd_knowledge_base.CreateKnowledgeBaseRequest], + gcd_knowledge_base.KnowledgeBase, + ]: + r"""Return a callable for the create knowledge base method over gRPC. + + Creates a knowledge base. + + Returns: + Callable[[~.CreateKnowledgeBaseRequest], + ~.KnowledgeBase]: + 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_knowledge_base" not in self._stubs: + self._stubs["create_knowledge_base"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.KnowledgeBases/CreateKnowledgeBase", + request_serializer=gcd_knowledge_base.CreateKnowledgeBaseRequest.serialize, + response_deserializer=gcd_knowledge_base.KnowledgeBase.deserialize, + ) + return self._stubs["create_knowledge_base"] + + @property + def delete_knowledge_base( + self, + ) -> Callable[[knowledge_base.DeleteKnowledgeBaseRequest], empty.Empty]: + r"""Return a callable for the delete knowledge base method over gRPC. + + Deletes the specified knowledge base. + + Returns: + Callable[[~.DeleteKnowledgeBaseRequest], + ~.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_knowledge_base" not in self._stubs: + self._stubs["delete_knowledge_base"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.KnowledgeBases/DeleteKnowledgeBase", + request_serializer=knowledge_base.DeleteKnowledgeBaseRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_knowledge_base"] + + @property + def update_knowledge_base( + self, + ) -> Callable[ + [gcd_knowledge_base.UpdateKnowledgeBaseRequest], + gcd_knowledge_base.KnowledgeBase, + ]: + r"""Return a callable for the update knowledge base method over gRPC. + + Updates the specified knowledge base. + + Returns: + Callable[[~.UpdateKnowledgeBaseRequest], + ~.KnowledgeBase]: + 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_knowledge_base" not in self._stubs: + self._stubs["update_knowledge_base"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.KnowledgeBases/UpdateKnowledgeBase", + request_serializer=gcd_knowledge_base.UpdateKnowledgeBaseRequest.serialize, + response_deserializer=gcd_knowledge_base.KnowledgeBase.deserialize, + ) + return self._stubs["update_knowledge_base"] + + +__all__ = ("KnowledgeBasesGrpcTransport",) diff --git a/google/cloud/dialogflow_v2/services/knowledge_bases/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/knowledge_bases/transports/grpc_asyncio.py new file mode 100644 index 000000000..3279f870f --- /dev/null +++ b/google/cloud/dialogflow_v2/services/knowledge_bases/transports/grpc_asyncio.py @@ -0,0 +1,402 @@ +# -*- 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.dialogflow_v2.types import knowledge_base +from google.cloud.dialogflow_v2.types import knowledge_base as gcd_knowledge_base +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import KnowledgeBasesTransport, DEFAULT_CLIENT_INFO +from .grpc import KnowledgeBasesGrpcTransport + + +class KnowledgeBasesGrpcAsyncIOTransport(KnowledgeBasesTransport): + """gRPC AsyncIO backend transport for KnowledgeBases. + + Service for managing + [KnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBase]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + self._stubs = {} + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def list_knowledge_bases( + self, + ) -> Callable[ + [knowledge_base.ListKnowledgeBasesRequest], + Awaitable[knowledge_base.ListKnowledgeBasesResponse], + ]: + r"""Return a callable for the list knowledge bases method over gRPC. + + Returns the list of all knowledge bases of the + specified agent. + + Returns: + Callable[[~.ListKnowledgeBasesRequest], + Awaitable[~.ListKnowledgeBasesResponse]]: + 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_knowledge_bases" not in self._stubs: + self._stubs["list_knowledge_bases"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.KnowledgeBases/ListKnowledgeBases", + request_serializer=knowledge_base.ListKnowledgeBasesRequest.serialize, + response_deserializer=knowledge_base.ListKnowledgeBasesResponse.deserialize, + ) + return self._stubs["list_knowledge_bases"] + + @property + def get_knowledge_base( + self, + ) -> Callable[ + [knowledge_base.GetKnowledgeBaseRequest], + Awaitable[knowledge_base.KnowledgeBase], + ]: + r"""Return a callable for the get knowledge base method over gRPC. + + Retrieves the specified knowledge base. + + Returns: + Callable[[~.GetKnowledgeBaseRequest], + Awaitable[~.KnowledgeBase]]: + 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_knowledge_base" not in self._stubs: + self._stubs["get_knowledge_base"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.KnowledgeBases/GetKnowledgeBase", + request_serializer=knowledge_base.GetKnowledgeBaseRequest.serialize, + response_deserializer=knowledge_base.KnowledgeBase.deserialize, + ) + return self._stubs["get_knowledge_base"] + + @property + def create_knowledge_base( + self, + ) -> Callable[ + [gcd_knowledge_base.CreateKnowledgeBaseRequest], + Awaitable[gcd_knowledge_base.KnowledgeBase], + ]: + r"""Return a callable for the create knowledge base method over gRPC. + + Creates a knowledge base. + + Returns: + Callable[[~.CreateKnowledgeBaseRequest], + Awaitable[~.KnowledgeBase]]: + 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_knowledge_base" not in self._stubs: + self._stubs["create_knowledge_base"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.KnowledgeBases/CreateKnowledgeBase", + request_serializer=gcd_knowledge_base.CreateKnowledgeBaseRequest.serialize, + response_deserializer=gcd_knowledge_base.KnowledgeBase.deserialize, + ) + return self._stubs["create_knowledge_base"] + + @property + def delete_knowledge_base( + self, + ) -> Callable[[knowledge_base.DeleteKnowledgeBaseRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete knowledge base method over gRPC. + + Deletes the specified knowledge base. + + Returns: + Callable[[~.DeleteKnowledgeBaseRequest], + 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_knowledge_base" not in self._stubs: + self._stubs["delete_knowledge_base"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.KnowledgeBases/DeleteKnowledgeBase", + request_serializer=knowledge_base.DeleteKnowledgeBaseRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_knowledge_base"] + + @property + def update_knowledge_base( + self, + ) -> Callable[ + [gcd_knowledge_base.UpdateKnowledgeBaseRequest], + Awaitable[gcd_knowledge_base.KnowledgeBase], + ]: + r"""Return a callable for the update knowledge base method over gRPC. + + Updates the specified knowledge base. + + Returns: + Callable[[~.UpdateKnowledgeBaseRequest], + Awaitable[~.KnowledgeBase]]: + 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_knowledge_base" not in self._stubs: + self._stubs["update_knowledge_base"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.KnowledgeBases/UpdateKnowledgeBase", + request_serializer=gcd_knowledge_base.UpdateKnowledgeBaseRequest.serialize, + response_deserializer=gcd_knowledge_base.KnowledgeBase.deserialize, + ) + return self._stubs["update_knowledge_base"] + + +__all__ = ("KnowledgeBasesGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflow_v2/services/participants/__init__.py b/google/cloud/dialogflow_v2/services/participants/__init__.py new file mode 100644 index 000000000..9ebfb1f70 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/participants/__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 ParticipantsClient +from .async_client import ParticipantsAsyncClient + +__all__ = ( + "ParticipantsClient", + "ParticipantsAsyncClient", +) diff --git a/google/cloud/dialogflow_v2/services/participants/async_client.py b/google/cloud/dialogflow_v2/services/participants/async_client.py new file mode 100644 index 000000000..ef57c3d3c --- /dev/null +++ b/google/cloud/dialogflow_v2/services/participants/async_client.py @@ -0,0 +1,893 @@ +# -*- 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.dialogflow_v2.services.participants import pagers +from google.cloud.dialogflow_v2.types import participant +from google.cloud.dialogflow_v2.types import participant as gcd_participant +from google.cloud.dialogflow_v2.types import session +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import ParticipantsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import ParticipantsGrpcAsyncIOTransport +from .client import ParticipantsClient + + +class ParticipantsAsyncClient: + """Service for managing + [Participants][google.cloud.dialogflow.v2.Participant]. + """ + + _client: ParticipantsClient + + DEFAULT_ENDPOINT = ParticipantsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = ParticipantsClient.DEFAULT_MTLS_ENDPOINT + + context_path = staticmethod(ParticipantsClient.context_path) + parse_context_path = staticmethod(ParticipantsClient.parse_context_path) + intent_path = staticmethod(ParticipantsClient.intent_path) + parse_intent_path = staticmethod(ParticipantsClient.parse_intent_path) + message_path = staticmethod(ParticipantsClient.message_path) + parse_message_path = staticmethod(ParticipantsClient.parse_message_path) + participant_path = staticmethod(ParticipantsClient.participant_path) + parse_participant_path = staticmethod(ParticipantsClient.parse_participant_path) + session_entity_type_path = staticmethod(ParticipantsClient.session_entity_type_path) + parse_session_entity_type_path = staticmethod( + ParticipantsClient.parse_session_entity_type_path + ) + + common_billing_account_path = staticmethod( + ParticipantsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + ParticipantsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(ParticipantsClient.common_folder_path) + parse_common_folder_path = staticmethod(ParticipantsClient.parse_common_folder_path) + + common_organization_path = staticmethod(ParticipantsClient.common_organization_path) + parse_common_organization_path = staticmethod( + ParticipantsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(ParticipantsClient.common_project_path) + parse_common_project_path = staticmethod( + ParticipantsClient.parse_common_project_path + ) + + common_location_path = staticmethod(ParticipantsClient.common_location_path) + parse_common_location_path = staticmethod( + ParticipantsClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ParticipantsAsyncClient: The constructed client. + """ + return ParticipantsClient.from_service_account_info.__func__(ParticipantsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ParticipantsAsyncClient: The constructed client. + """ + return ParticipantsClient.from_service_account_file.__func__(ParticipantsAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> ParticipantsTransport: + """Return the transport used by the client instance. + + Returns: + ParticipantsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(ParticipantsClient).get_transport_class, type(ParticipantsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, ParticipantsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the participants 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, ~.ParticipantsTransport]): 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 = ParticipantsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def create_participant( + self, + request: gcd_participant.CreateParticipantRequest = None, + *, + parent: str = None, + participant: gcd_participant.Participant = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.Participant: + r"""Creates a new participant in a conversation. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.CreateParticipantRequest`): + The request object. The request message for + [Participants.CreateParticipant][google.cloud.dialogflow.v2.Participants.CreateParticipant]. + parent (:class:`str`): + Required. Resource identifier of the conversation adding + the participant. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + participant (:class:`google.cloud.dialogflow_v2.types.Participant`): + Required. The participant to create. + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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, participant]) + if request is 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 = gcd_participant.CreateParticipantRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if participant is not None: + request.participant = participant + + # 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_participant, + 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_participant( + self, + request: participant.GetParticipantRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.Participant: + r"""Retrieves a conversation participant. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.GetParticipantRequest`): + The request object. The request message for + [Participants.GetParticipant][google.cloud.dialogflow.v2.Participants.GetParticipant]. + name (:class:`str`): + Required. The name of the participant. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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 = participant.GetParticipantRequest(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_participant, + 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 list_participants( + self, + request: participant.ListParticipantsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListParticipantsAsyncPager: + r"""Returns the list of all participants in the specified + conversation. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.ListParticipantsRequest`): + The request object. The request message for + [Participants.ListParticipants][google.cloud.dialogflow.v2.Participants.ListParticipants]. + parent (:class:`str`): + Required. The conversation to list all participants + from. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.services.participants.pagers.ListParticipantsAsyncPager: + The response message for + [Participants.ListParticipants][google.cloud.dialogflow.v2.Participants.ListParticipants]. + + 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 = participant.ListParticipantsRequest(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_participants, + 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.ListParticipantsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def update_participant( + self, + request: gcd_participant.UpdateParticipantRequest = None, + *, + participant: gcd_participant.Participant = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.Participant: + r"""Updates the specified participant. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.UpdateParticipantRequest`): + The request object. The request message for + [Participants.UpdateParticipant][google.cloud.dialogflow.v2.Participants.UpdateParticipant]. + participant (:class:`google.cloud.dialogflow_v2.types.Participant`): + Required. The participant to update. + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): + Required. The mask to specify which + fields to update. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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([participant, 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 = gcd_participant.UpdateParticipantRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if participant is not None: + request.participant = participant + 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_participant, + 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( + (("participant.name", request.participant.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def analyze_content( + self, + request: gcd_participant.AnalyzeContentRequest = None, + *, + participant: str = None, + text_input: session.TextInput = None, + audio_input: gcd_participant.AudioInput = None, + event_input: session.EventInput = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.AnalyzeContentResponse: + r"""Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Args: + request (:class:`google.cloud.dialogflow_v2.types.AnalyzeContentRequest`): + The request object. The request message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent]. + participant (:class:`str`): + Required. The name of the participant this text comes + from. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + text_input (:class:`google.cloud.dialogflow_v2.types.TextInput`): + The natural language text to be + processed. + + This corresponds to the ``text_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + audio_input (:class:`google.cloud.dialogflow_v2.types.AudioInput`): + The natural language speech audio to + be processed. + + This corresponds to the ``audio_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + event_input (:class:`google.cloud.dialogflow_v2.types.EventInput`): + An input event to send to Dialogflow. + This corresponds to the ``event_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.AnalyzeContentResponse: + The response message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent]. + + """ + # Create or coerce a 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([participant, text_input, audio_input, event_input]) + if request is 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 = gcd_participant.AnalyzeContentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if participant is not None: + request.participant = participant + if text_input is not None: + request.text_input = text_input + if audio_input is not None: + request.audio_input = audio_input + if event_input is not None: + request.event_input = event_input + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.analyze_content, + 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( + (("participant", request.participant),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def streaming_analyze_content( + self, + requests: AsyncIterator[participant.StreamingAnalyzeContentRequest] = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> Awaitable[AsyncIterable[participant.StreamingAnalyzeContentResponse]]: + r"""Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. Note: + This method is only available through the gRPC API (not REST). + + The top-level message sent to the client by the server is + ``StreamingAnalyzeContentResponse``. Multiple response messages + can be returned in order. The first one or more messages contain + the ``recognition_result`` field. Each result represents a more + complete transcript of what the user said. The next message + contains the ``reply_text`` field and potentially the + ``reply_audio`` field. The message can also contain the + ``automated_agent_reply`` field. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Args: + requests (AsyncIterator[`google.cloud.dialogflow_v2.types.StreamingAnalyzeContentRequest`]): + The request object AsyncIterator. The top-level message sent by the + client to the + [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2.Participants.StreamingAnalyzeContent] + method. + Multiple request messages should be sent in order: + + 1. The first message must contain + [participant][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.participant], + [config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] + and optionally + [query_params][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.query_params]. + If you want to receive an audio response, it should + also contain + [reply_audio_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.reply_audio_config]. + The message must not contain + [input][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input]. + 2. If + [config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] + in the first message was set to + [audio_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.audio_config], + all subsequent messages must contain + [input_audio][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input_audio] + to continue with Speech recognition. + However, note that: + + * Dialogflow will bill you for the audio so far. + * Dialogflow discards all Speech recognition results + in favor of the text input. + + 3. If + [StreamingAnalyzeContentRequest.config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] + in the first message was set to + [StreamingAnalyzeContentRequest.text_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.text_config], + then the second message must contain only + [input_text][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input_text]. + Moreover, you must not send more than two messages. + 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[google.cloud.dialogflow_v2.types.StreamingAnalyzeContentResponse]: + The top-level message returned from the + StreamingAnalyzeContent 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 next message contains reply_text and + optionally reply_audio returned by an agent. This + message may also contain automated_agent_reply. + + """ + + # 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_analyze_content, + 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 suggest_articles( + self, + request: participant.SuggestArticlesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.SuggestArticlesResponse: + r"""Gets suggested articles for a participant based on + specific historical messages. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.SuggestArticlesRequest`): + The request object. The request message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2.Participants.SuggestArticles]. + parent (:class:`str`): + Required. The name of the participant to fetch + suggestion for. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.SuggestArticlesResponse: + The response message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2.Participants.SuggestArticles]. + + """ + # Create or coerce a 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 = participant.SuggestArticlesRequest(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.suggest_articles, + 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 suggest_faq_answers( + self, + request: participant.SuggestFaqAnswersRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.SuggestFaqAnswersResponse: + r"""Gets suggested faq answers for a participant based on + specific historical messages. + + Args: + request (:class:`google.cloud.dialogflow_v2.types.SuggestFaqAnswersRequest`): + The request object. The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2.Participants.SuggestFaqAnswers]. + parent (:class:`str`): + Required. The name of the participant to fetch + suggestion for. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.SuggestFaqAnswersResponse: + The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2.Participants.SuggestFaqAnswers]. + + """ + # Create or coerce a 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 = participant.SuggestFaqAnswersRequest(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.suggest_faq_answers, + 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 + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ParticipantsAsyncClient",) diff --git a/google/cloud/dialogflow_v2/services/participants/client.py b/google/cloud/dialogflow_v2/services/participants/client.py new file mode 100644 index 000000000..086d9909c --- /dev/null +++ b/google/cloud/dialogflow_v2/services/participants/client.py @@ -0,0 +1,1120 @@ +# -*- 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.dialogflow_v2.services.participants import pagers +from google.cloud.dialogflow_v2.types import participant +from google.cloud.dialogflow_v2.types import participant as gcd_participant +from google.cloud.dialogflow_v2.types import session +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import ParticipantsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import ParticipantsGrpcTransport +from .transports.grpc_asyncio import ParticipantsGrpcAsyncIOTransport + + +class ParticipantsClientMeta(type): + """Metaclass for the Participants 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[ParticipantsTransport]] + _transport_registry["grpc"] = ParticipantsGrpcTransport + _transport_registry["grpc_asyncio"] = ParticipantsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[ParticipantsTransport]: + """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 ParticipantsClient(metaclass=ParticipantsClientMeta): + """Service for managing + [Participants][google.cloud.dialogflow.v2.Participant]. + """ + + @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_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ParticipantsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ParticipantsClient: 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) -> ParticipantsTransport: + """Return the transport used by the client instance. + + Returns: + ParticipantsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def context_path(project: str, session: str, context: str,) -> str: + """Return a fully-qualified context string.""" + return "projects/{project}/agent/sessions/{session}/contexts/{context}".format( + project=project, session=session, context=context, + ) + + @staticmethod + def parse_context_path(path: str) -> Dict[str, str]: + """Parse a context path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/agent/sessions/(?P.+?)/contexts/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def intent_path(project: str, intent: str,) -> str: + """Return a fully-qualified intent string.""" + return "projects/{project}/agent/intents/{intent}".format( + project=project, 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.+?)/agent/intents/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def message_path(project: str, conversation: str, message: str,) -> str: + """Return a fully-qualified message string.""" + return "projects/{project}/conversations/{conversation}/messages/{message}".format( + project=project, conversation=conversation, message=message, + ) + + @staticmethod + def parse_message_path(path: str) -> Dict[str, str]: + """Parse a message path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversations/(?P.+?)/messages/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def participant_path(project: str, conversation: str, participant: str,) -> str: + """Return a fully-qualified participant string.""" + return "projects/{project}/conversations/{conversation}/participants/{participant}".format( + project=project, conversation=conversation, participant=participant, + ) + + @staticmethod + def parse_participant_path(path: str) -> Dict[str, str]: + """Parse a participant path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversations/(?P.+?)/participants/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def session_entity_type_path(project: str, session: str, entity_type: str,) -> str: + """Return a fully-qualified session_entity_type string.""" + return "projects/{project}/agent/sessions/{session}/entityTypes/{entity_type}".format( + project=project, 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.+?)/agent/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, ParticipantsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the participants 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, ParticipantsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, ParticipantsTransport): + # transport is a ParticipantsTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def create_participant( + self, + request: gcd_participant.CreateParticipantRequest = None, + *, + parent: str = None, + participant: gcd_participant.Participant = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.Participant: + r"""Creates a new participant in a conversation. + + Args: + request (google.cloud.dialogflow_v2.types.CreateParticipantRequest): + The request object. The request message for + [Participants.CreateParticipant][google.cloud.dialogflow.v2.Participants.CreateParticipant]. + parent (str): + Required. Resource identifier of the conversation adding + the participant. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + participant (google.cloud.dialogflow_v2.types.Participant): + Required. The participant to create. + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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, participant]) + if request is 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 gcd_participant.CreateParticipantRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_participant.CreateParticipantRequest): + request = gcd_participant.CreateParticipantRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if participant is not None: + request.participant = participant + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_participant] + + # 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_participant( + self, + request: participant.GetParticipantRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.Participant: + r"""Retrieves a conversation participant. + + Args: + request (google.cloud.dialogflow_v2.types.GetParticipantRequest): + The request object. The request message for + [Participants.GetParticipant][google.cloud.dialogflow.v2.Participants.GetParticipant]. + name (str): + Required. The name of the participant. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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 participant.GetParticipantRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, participant.GetParticipantRequest): + request = participant.GetParticipantRequest(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_participant] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def list_participants( + self, + request: participant.ListParticipantsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListParticipantsPager: + r"""Returns the list of all participants in the specified + conversation. + + Args: + request (google.cloud.dialogflow_v2.types.ListParticipantsRequest): + The request object. The request message for + [Participants.ListParticipants][google.cloud.dialogflow.v2.Participants.ListParticipants]. + parent (str): + Required. The conversation to list all participants + from. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.services.participants.pagers.ListParticipantsPager: + The response message for + [Participants.ListParticipants][google.cloud.dialogflow.v2.Participants.ListParticipants]. + + 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 participant.ListParticipantsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, participant.ListParticipantsRequest): + request = participant.ListParticipantsRequest(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_participants] + + # 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.ListParticipantsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def update_participant( + self, + request: gcd_participant.UpdateParticipantRequest = None, + *, + participant: gcd_participant.Participant = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.Participant: + r"""Updates the specified participant. + + Args: + request (google.cloud.dialogflow_v2.types.UpdateParticipantRequest): + The request object. The request message for + [Participants.UpdateParticipant][google.cloud.dialogflow.v2.Participants.UpdateParticipant]. + participant (google.cloud.dialogflow_v2.types.Participant): + Required. The participant to update. + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. The mask to specify which + fields to update. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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([participant, 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 gcd_participant.UpdateParticipantRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_participant.UpdateParticipantRequest): + request = gcd_participant.UpdateParticipantRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if participant is not None: + request.participant = participant + 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_participant] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("participant.name", request.participant.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def analyze_content( + self, + request: gcd_participant.AnalyzeContentRequest = None, + *, + participant: str = None, + text_input: session.TextInput = None, + audio_input: gcd_participant.AudioInput = None, + event_input: session.EventInput = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.AnalyzeContentResponse: + r"""Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Args: + request (google.cloud.dialogflow_v2.types.AnalyzeContentRequest): + The request object. The request message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent]. + participant (str): + Required. The name of the participant this text comes + from. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + text_input (google.cloud.dialogflow_v2.types.TextInput): + The natural language text to be + processed. + + This corresponds to the ``text_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + audio_input (google.cloud.dialogflow_v2.types.AudioInput): + The natural language speech audio to + be processed. + + This corresponds to the ``audio_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + event_input (google.cloud.dialogflow_v2.types.EventInput): + An input event to send to Dialogflow. + This corresponds to the ``event_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.AnalyzeContentResponse: + The response message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent]. + + """ + # Create or coerce a 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([participant, text_input, audio_input, event_input]) + if request is 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 gcd_participant.AnalyzeContentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_participant.AnalyzeContentRequest): + request = gcd_participant.AnalyzeContentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if participant is not None: + request.participant = participant + if text_input is not None: + request.text_input = text_input + if audio_input is not None: + request.audio_input = audio_input + if event_input is not None: + request.event_input = event_input + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.analyze_content] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("participant", request.participant),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def streaming_analyze_content( + self, + requests: Iterator[participant.StreamingAnalyzeContentRequest] = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> Iterable[participant.StreamingAnalyzeContentResponse]: + r"""Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. Note: + This method is only available through the gRPC API (not REST). + + The top-level message sent to the client by the server is + ``StreamingAnalyzeContentResponse``. Multiple response messages + can be returned in order. The first one or more messages contain + the ``recognition_result`` field. Each result represents a more + complete transcript of what the user said. The next message + contains the ``reply_text`` field and potentially the + ``reply_audio`` field. The message can also contain the + ``automated_agent_reply`` field. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Args: + requests (Iterator[google.cloud.dialogflow_v2.types.StreamingAnalyzeContentRequest]): + The request object iterator. The top-level message sent by the + client to the + [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2.Participants.StreamingAnalyzeContent] + method. + Multiple request messages should be sent in order: + + 1. The first message must contain + [participant][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.participant], + [config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] + and optionally + [query_params][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.query_params]. + If you want to receive an audio response, it should + also contain + [reply_audio_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.reply_audio_config]. + The message must not contain + [input][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input]. + 2. If + [config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] + in the first message was set to + [audio_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.audio_config], + all subsequent messages must contain + [input_audio][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input_audio] + to continue with Speech recognition. + However, note that: + + * Dialogflow will bill you for the audio so far. + * Dialogflow discards all Speech recognition results + in favor of the text input. + + 3. If + [StreamingAnalyzeContentRequest.config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] + in the first message was set to + [StreamingAnalyzeContentRequest.text_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.text_config], + then the second message must contain only + [input_text][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input_text]. + Moreover, you must not send more than two messages. + 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[google.cloud.dialogflow_v2.types.StreamingAnalyzeContentResponse]: + The top-level message returned from the + StreamingAnalyzeContent 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 next message contains reply_text and + optionally reply_audio returned by an agent. This + message may also contain automated_agent_reply. + + """ + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[ + self._transport.streaming_analyze_content + ] + + # Send the request. + response = rpc(requests, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def suggest_articles( + self, + request: participant.SuggestArticlesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.SuggestArticlesResponse: + r"""Gets suggested articles for a participant based on + specific historical messages. + + Args: + request (google.cloud.dialogflow_v2.types.SuggestArticlesRequest): + The request object. The request message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2.Participants.SuggestArticles]. + parent (str): + Required. The name of the participant to fetch + suggestion for. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.SuggestArticlesResponse: + The response message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2.Participants.SuggestArticles]. + + """ + # Create or coerce a 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 participant.SuggestArticlesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, participant.SuggestArticlesRequest): + request = participant.SuggestArticlesRequest(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.suggest_articles] + + # 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 suggest_faq_answers( + self, + request: participant.SuggestFaqAnswersRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.SuggestFaqAnswersResponse: + r"""Gets suggested faq answers for a participant based on + specific historical messages. + + Args: + request (google.cloud.dialogflow_v2.types.SuggestFaqAnswersRequest): + The request object. The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2.Participants.SuggestFaqAnswers]. + parent (str): + Required. The name of the participant to fetch + suggestion for. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2.types.SuggestFaqAnswersResponse: + The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2.Participants.SuggestFaqAnswers]. + + """ + # Create or coerce a 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 participant.SuggestFaqAnswersRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, participant.SuggestFaqAnswersRequest): + request = participant.SuggestFaqAnswersRequest(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.suggest_faq_answers] + + # 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 + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ParticipantsClient",) diff --git a/google/cloud/dialogflow_v2/services/participants/pagers.py b/google/cloud/dialogflow_v2/services/participants/pagers.py new file mode 100644 index 000000000..48ce3c8ac --- /dev/null +++ b/google/cloud/dialogflow_v2/services/participants/pagers.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.dialogflow_v2.types import participant + + +class ListParticipantsPager: + """A pager for iterating through ``list_participants`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListParticipantsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``participants`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListParticipants`` requests and continue to iterate + through the ``participants`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListParticipantsResponse` + 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[..., participant.ListParticipantsResponse], + request: participant.ListParticipantsRequest, + response: participant.ListParticipantsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListParticipantsRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListParticipantsResponse): + 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 = participant.ListParticipantsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[participant.ListParticipantsResponse]: + 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[participant.Participant]: + for page in self.pages: + yield from page.participants + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListParticipantsAsyncPager: + """A pager for iterating through ``list_participants`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2.types.ListParticipantsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``participants`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListParticipants`` requests and continue to iterate + through the ``participants`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2.types.ListParticipantsResponse` + 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[participant.ListParticipantsResponse]], + request: participant.ListParticipantsRequest, + response: participant.ListParticipantsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2.types.ListParticipantsRequest): + The initial request object. + response (google.cloud.dialogflow_v2.types.ListParticipantsResponse): + 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 = participant.ListParticipantsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[participant.ListParticipantsResponse]: + 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[participant.Participant]: + async def async_generator(): + async for page in self.pages: + for response in page.participants: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflow_v2/services/participants/transports/__init__.py b/google/cloud/dialogflow_v2/services/participants/transports/__init__.py new file mode 100644 index 000000000..91a4cec1c --- /dev/null +++ b/google/cloud/dialogflow_v2/services/participants/transports/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import ParticipantsTransport +from .grpc import ParticipantsGrpcTransport +from .grpc_asyncio import ParticipantsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[ParticipantsTransport]] +_transport_registry["grpc"] = ParticipantsGrpcTransport +_transport_registry["grpc_asyncio"] = ParticipantsGrpcAsyncIOTransport + +__all__ = ( + "ParticipantsTransport", + "ParticipantsGrpcTransport", + "ParticipantsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflow_v2/services/participants/transports/base.py b/google/cloud/dialogflow_v2/services/participants/transports/base.py new file mode 100644 index 000000000..bf2219e05 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/participants/transports/base.py @@ -0,0 +1,243 @@ +# -*- 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.dialogflow_v2.types import participant +from google.cloud.dialogflow_v2.types import participant as gcd_participant + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class ParticipantsTransport(abc.ABC): + """Abstract transport class for Participants.""" + + 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_participant: gapic_v1.method.wrap_method( + self.create_participant, default_timeout=None, client_info=client_info, + ), + self.get_participant: gapic_v1.method.wrap_method( + self.get_participant, default_timeout=None, client_info=client_info, + ), + self.list_participants: gapic_v1.method.wrap_method( + self.list_participants, default_timeout=None, client_info=client_info, + ), + self.update_participant: gapic_v1.method.wrap_method( + self.update_participant, default_timeout=None, client_info=client_info, + ), + self.analyze_content: gapic_v1.method.wrap_method( + self.analyze_content, + 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_analyze_content: gapic_v1.method.wrap_method( + self.streaming_analyze_content, + default_timeout=220.0, + client_info=client_info, + ), + self.suggest_articles: gapic_v1.method.wrap_method( + self.suggest_articles, default_timeout=None, client_info=client_info, + ), + self.suggest_faq_answers: gapic_v1.method.wrap_method( + self.suggest_faq_answers, default_timeout=None, client_info=client_info, + ), + } + + @property + def create_participant( + self, + ) -> typing.Callable[ + [gcd_participant.CreateParticipantRequest], + typing.Union[ + gcd_participant.Participant, typing.Awaitable[gcd_participant.Participant] + ], + ]: + raise NotImplementedError() + + @property + def get_participant( + self, + ) -> typing.Callable[ + [participant.GetParticipantRequest], + typing.Union[ + participant.Participant, typing.Awaitable[participant.Participant] + ], + ]: + raise NotImplementedError() + + @property + def list_participants( + self, + ) -> typing.Callable[ + [participant.ListParticipantsRequest], + typing.Union[ + participant.ListParticipantsResponse, + typing.Awaitable[participant.ListParticipantsResponse], + ], + ]: + raise NotImplementedError() + + @property + def update_participant( + self, + ) -> typing.Callable[ + [gcd_participant.UpdateParticipantRequest], + typing.Union[ + gcd_participant.Participant, typing.Awaitable[gcd_participant.Participant] + ], + ]: + raise NotImplementedError() + + @property + def analyze_content( + self, + ) -> typing.Callable[ + [gcd_participant.AnalyzeContentRequest], + typing.Union[ + gcd_participant.AnalyzeContentResponse, + typing.Awaitable[gcd_participant.AnalyzeContentResponse], + ], + ]: + raise NotImplementedError() + + @property + def streaming_analyze_content( + self, + ) -> typing.Callable[ + [participant.StreamingAnalyzeContentRequest], + typing.Union[ + participant.StreamingAnalyzeContentResponse, + typing.Awaitable[participant.StreamingAnalyzeContentResponse], + ], + ]: + raise NotImplementedError() + + @property + def suggest_articles( + self, + ) -> typing.Callable[ + [participant.SuggestArticlesRequest], + typing.Union[ + participant.SuggestArticlesResponse, + typing.Awaitable[participant.SuggestArticlesResponse], + ], + ]: + raise NotImplementedError() + + @property + def suggest_faq_answers( + self, + ) -> typing.Callable[ + [participant.SuggestFaqAnswersRequest], + typing.Union[ + participant.SuggestFaqAnswersResponse, + typing.Awaitable[participant.SuggestFaqAnswersResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("ParticipantsTransport",) diff --git a/google/cloud/dialogflow_v2/services/participants/transports/grpc.py b/google/cloud/dialogflow_v2/services/participants/transports/grpc.py new file mode 100644 index 000000000..386acf35f --- /dev/null +++ b/google/cloud/dialogflow_v2/services/participants/transports/grpc.py @@ -0,0 +1,500 @@ +# -*- 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.dialogflow_v2.types import participant +from google.cloud.dialogflow_v2.types import participant as gcd_participant + +from .base import ParticipantsTransport, DEFAULT_CLIENT_INFO + + +class ParticipantsGrpcTransport(ParticipantsTransport): + """gRPC backend transport for Participants. + + Service for managing + [Participants][google.cloud.dialogflow.v2.Participant]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + self._stubs = {} # type: Dict[str, Callable] + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + @classmethod + def create_channel( + cls, + host: str = "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 (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def create_participant( + self, + ) -> Callable[ + [gcd_participant.CreateParticipantRequest], gcd_participant.Participant + ]: + r"""Return a callable for the create participant method over gRPC. + + Creates a new participant in a conversation. + + Returns: + Callable[[~.CreateParticipantRequest], + ~.Participant]: + 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_participant" not in self._stubs: + self._stubs["create_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/CreateParticipant", + request_serializer=gcd_participant.CreateParticipantRequest.serialize, + response_deserializer=gcd_participant.Participant.deserialize, + ) + return self._stubs["create_participant"] + + @property + def get_participant( + self, + ) -> Callable[[participant.GetParticipantRequest], participant.Participant]: + r"""Return a callable for the get participant method over gRPC. + + Retrieves a conversation participant. + + Returns: + Callable[[~.GetParticipantRequest], + ~.Participant]: + 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_participant" not in self._stubs: + self._stubs["get_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/GetParticipant", + request_serializer=participant.GetParticipantRequest.serialize, + response_deserializer=participant.Participant.deserialize, + ) + return self._stubs["get_participant"] + + @property + def list_participants( + self, + ) -> Callable[ + [participant.ListParticipantsRequest], participant.ListParticipantsResponse + ]: + r"""Return a callable for the list participants method over gRPC. + + Returns the list of all participants in the specified + conversation. + + Returns: + Callable[[~.ListParticipantsRequest], + ~.ListParticipantsResponse]: + 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_participants" not in self._stubs: + self._stubs["list_participants"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/ListParticipants", + request_serializer=participant.ListParticipantsRequest.serialize, + response_deserializer=participant.ListParticipantsResponse.deserialize, + ) + return self._stubs["list_participants"] + + @property + def update_participant( + self, + ) -> Callable[ + [gcd_participant.UpdateParticipantRequest], gcd_participant.Participant + ]: + r"""Return a callable for the update participant method over gRPC. + + Updates the specified participant. + + Returns: + Callable[[~.UpdateParticipantRequest], + ~.Participant]: + 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_participant" not in self._stubs: + self._stubs["update_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/UpdateParticipant", + request_serializer=gcd_participant.UpdateParticipantRequest.serialize, + response_deserializer=gcd_participant.Participant.deserialize, + ) + return self._stubs["update_participant"] + + @property + def analyze_content( + self, + ) -> Callable[ + [gcd_participant.AnalyzeContentRequest], gcd_participant.AnalyzeContentResponse + ]: + r"""Return a callable for the analyze content method over gRPC. + + Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Returns: + Callable[[~.AnalyzeContentRequest], + ~.AnalyzeContentResponse]: + 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 "analyze_content" not in self._stubs: + self._stubs["analyze_content"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/AnalyzeContent", + request_serializer=gcd_participant.AnalyzeContentRequest.serialize, + response_deserializer=gcd_participant.AnalyzeContentResponse.deserialize, + ) + return self._stubs["analyze_content"] + + @property + def streaming_analyze_content( + self, + ) -> Callable[ + [participant.StreamingAnalyzeContentRequest], + participant.StreamingAnalyzeContentResponse, + ]: + r"""Return a callable for the streaming analyze content method over gRPC. + + Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. Note: + This method is only available through the gRPC API (not REST). + + The top-level message sent to the client by the server is + ``StreamingAnalyzeContentResponse``. Multiple response messages + can be returned in order. The first one or more messages contain + the ``recognition_result`` field. Each result represents a more + complete transcript of what the user said. The next message + contains the ``reply_text`` field and potentially the + ``reply_audio`` field. The message can also contain the + ``automated_agent_reply`` field. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Returns: + Callable[[~.StreamingAnalyzeContentRequest], + ~.StreamingAnalyzeContentResponse]: + 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_analyze_content" not in self._stubs: + self._stubs["streaming_analyze_content"] = self.grpc_channel.stream_stream( + "/google.cloud.dialogflow.v2.Participants/StreamingAnalyzeContent", + request_serializer=participant.StreamingAnalyzeContentRequest.serialize, + response_deserializer=participant.StreamingAnalyzeContentResponse.deserialize, + ) + return self._stubs["streaming_analyze_content"] + + @property + def suggest_articles( + self, + ) -> Callable[ + [participant.SuggestArticlesRequest], participant.SuggestArticlesResponse + ]: + r"""Return a callable for the suggest articles method over gRPC. + + Gets suggested articles for a participant based on + specific historical messages. + + Returns: + Callable[[~.SuggestArticlesRequest], + ~.SuggestArticlesResponse]: + 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 "suggest_articles" not in self._stubs: + self._stubs["suggest_articles"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/SuggestArticles", + request_serializer=participant.SuggestArticlesRequest.serialize, + response_deserializer=participant.SuggestArticlesResponse.deserialize, + ) + return self._stubs["suggest_articles"] + + @property + def suggest_faq_answers( + self, + ) -> Callable[ + [participant.SuggestFaqAnswersRequest], participant.SuggestFaqAnswersResponse + ]: + r"""Return a callable for the suggest faq answers method over gRPC. + + Gets suggested faq answers for a participant based on + specific historical messages. + + Returns: + Callable[[~.SuggestFaqAnswersRequest], + ~.SuggestFaqAnswersResponse]: + 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 "suggest_faq_answers" not in self._stubs: + self._stubs["suggest_faq_answers"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/SuggestFaqAnswers", + request_serializer=participant.SuggestFaqAnswersRequest.serialize, + response_deserializer=participant.SuggestFaqAnswersResponse.deserialize, + ) + return self._stubs["suggest_faq_answers"] + + +__all__ = ("ParticipantsGrpcTransport",) diff --git a/google/cloud/dialogflow_v2/services/participants/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/participants/transports/grpc_asyncio.py new file mode 100644 index 000000000..b00793e84 --- /dev/null +++ b/google/cloud/dialogflow_v2/services/participants/transports/grpc_asyncio.py @@ -0,0 +1,512 @@ +# -*- 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.dialogflow_v2.types import participant +from google.cloud.dialogflow_v2.types import participant as gcd_participant + +from .base import ParticipantsTransport, DEFAULT_CLIENT_INFO +from .grpc import ParticipantsGrpcTransport + + +class ParticipantsGrpcAsyncIOTransport(ParticipantsTransport): + """gRPC AsyncIO backend transport for Participants. + + Service for managing + [Participants][google.cloud.dialogflow.v2.Participant]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + self._stubs = {} + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def create_participant( + self, + ) -> Callable[ + [gcd_participant.CreateParticipantRequest], + Awaitable[gcd_participant.Participant], + ]: + r"""Return a callable for the create participant method over gRPC. + + Creates a new participant in a conversation. + + Returns: + Callable[[~.CreateParticipantRequest], + Awaitable[~.Participant]]: + 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_participant" not in self._stubs: + self._stubs["create_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/CreateParticipant", + request_serializer=gcd_participant.CreateParticipantRequest.serialize, + response_deserializer=gcd_participant.Participant.deserialize, + ) + return self._stubs["create_participant"] + + @property + def get_participant( + self, + ) -> Callable[ + [participant.GetParticipantRequest], Awaitable[participant.Participant] + ]: + r"""Return a callable for the get participant method over gRPC. + + Retrieves a conversation participant. + + Returns: + Callable[[~.GetParticipantRequest], + Awaitable[~.Participant]]: + 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_participant" not in self._stubs: + self._stubs["get_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/GetParticipant", + request_serializer=participant.GetParticipantRequest.serialize, + response_deserializer=participant.Participant.deserialize, + ) + return self._stubs["get_participant"] + + @property + def list_participants( + self, + ) -> Callable[ + [participant.ListParticipantsRequest], + Awaitable[participant.ListParticipantsResponse], + ]: + r"""Return a callable for the list participants method over gRPC. + + Returns the list of all participants in the specified + conversation. + + Returns: + Callable[[~.ListParticipantsRequest], + Awaitable[~.ListParticipantsResponse]]: + 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_participants" not in self._stubs: + self._stubs["list_participants"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/ListParticipants", + request_serializer=participant.ListParticipantsRequest.serialize, + response_deserializer=participant.ListParticipantsResponse.deserialize, + ) + return self._stubs["list_participants"] + + @property + def update_participant( + self, + ) -> Callable[ + [gcd_participant.UpdateParticipantRequest], + Awaitable[gcd_participant.Participant], + ]: + r"""Return a callable for the update participant method over gRPC. + + Updates the specified participant. + + Returns: + Callable[[~.UpdateParticipantRequest], + Awaitable[~.Participant]]: + 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_participant" not in self._stubs: + self._stubs["update_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/UpdateParticipant", + request_serializer=gcd_participant.UpdateParticipantRequest.serialize, + response_deserializer=gcd_participant.Participant.deserialize, + ) + return self._stubs["update_participant"] + + @property + def analyze_content( + self, + ) -> Callable[ + [gcd_participant.AnalyzeContentRequest], + Awaitable[gcd_participant.AnalyzeContentResponse], + ]: + r"""Return a callable for the analyze content method over gRPC. + + Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Returns: + Callable[[~.AnalyzeContentRequest], + Awaitable[~.AnalyzeContentResponse]]: + 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 "analyze_content" not in self._stubs: + self._stubs["analyze_content"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/AnalyzeContent", + request_serializer=gcd_participant.AnalyzeContentRequest.serialize, + response_deserializer=gcd_participant.AnalyzeContentResponse.deserialize, + ) + return self._stubs["analyze_content"] + + @property + def streaming_analyze_content( + self, + ) -> Callable[ + [participant.StreamingAnalyzeContentRequest], + Awaitable[participant.StreamingAnalyzeContentResponse], + ]: + r"""Return a callable for the streaming analyze content method over gRPC. + + Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. Note: + This method is only available through the gRPC API (not REST). + + The top-level message sent to the client by the server is + ``StreamingAnalyzeContentResponse``. Multiple response messages + can be returned in order. The first one or more messages contain + the ``recognition_result`` field. Each result represents a more + complete transcript of what the user said. The next message + contains the ``reply_text`` field and potentially the + ``reply_audio`` field. The message can also contain the + ``automated_agent_reply`` field. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Returns: + Callable[[~.StreamingAnalyzeContentRequest], + Awaitable[~.StreamingAnalyzeContentResponse]]: + 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_analyze_content" not in self._stubs: + self._stubs["streaming_analyze_content"] = self.grpc_channel.stream_stream( + "/google.cloud.dialogflow.v2.Participants/StreamingAnalyzeContent", + request_serializer=participant.StreamingAnalyzeContentRequest.serialize, + response_deserializer=participant.StreamingAnalyzeContentResponse.deserialize, + ) + return self._stubs["streaming_analyze_content"] + + @property + def suggest_articles( + self, + ) -> Callable[ + [participant.SuggestArticlesRequest], + Awaitable[participant.SuggestArticlesResponse], + ]: + r"""Return a callable for the suggest articles method over gRPC. + + Gets suggested articles for a participant based on + specific historical messages. + + Returns: + Callable[[~.SuggestArticlesRequest], + Awaitable[~.SuggestArticlesResponse]]: + 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 "suggest_articles" not in self._stubs: + self._stubs["suggest_articles"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/SuggestArticles", + request_serializer=participant.SuggestArticlesRequest.serialize, + response_deserializer=participant.SuggestArticlesResponse.deserialize, + ) + return self._stubs["suggest_articles"] + + @property + def suggest_faq_answers( + self, + ) -> Callable[ + [participant.SuggestFaqAnswersRequest], + Awaitable[participant.SuggestFaqAnswersResponse], + ]: + r"""Return a callable for the suggest faq answers method over gRPC. + + Gets suggested faq answers for a participant based on + specific historical messages. + + Returns: + Callable[[~.SuggestFaqAnswersRequest], + Awaitable[~.SuggestFaqAnswersResponse]]: + 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 "suggest_faq_answers" not in self._stubs: + self._stubs["suggest_faq_answers"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2.Participants/SuggestFaqAnswers", + request_serializer=participant.SuggestFaqAnswersRequest.serialize, + response_deserializer=participant.SuggestFaqAnswersResponse.deserialize, + ) + return self._stubs["suggest_faq_answers"] + + +__all__ = ("ParticipantsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflow_v2/services/session_entity_types/async_client.py b/google/cloud/dialogflow_v2/services/session_entity_types/async_client.py index c0fa9de2b..0eb8e3f7c 100644 --- a/google/cloud/dialogflow_v2/services/session_entity_types/async_client.py +++ b/google/cloud/dialogflow_v2/services/session_entity_types/async_client.py @@ -87,7 +87,36 @@ class SessionEntityTypesAsyncClient: SessionEntityTypesClient.parse_common_location_path ) - from_service_account_file = SessionEntityTypesClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionEntityTypesAsyncClient: The constructed client. + """ + return SessionEntityTypesClient.from_service_account_info.__func__(SessionEntityTypesAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionEntityTypesAsyncClient: The constructed client. + """ + return SessionEntityTypesClient.from_service_account_file.__func__(SessionEntityTypesAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -168,7 +197,7 @@ async def list_session_entity_types( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + request (:class:`google.cloud.dialogflow_v2.types.ListSessionEntityTypesRequest`): The request object. The request message for [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.v2.SessionEntityTypes.ListSessionEntityTypes]. parent (:class:`str`): @@ -179,6 +208,7 @@ async def list_session_entity_types( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -190,7 +220,7 @@ async def list_session_entity_types( sent along with the request as metadata. Returns: - ~.pagers.ListSessionEntityTypesAsyncPager: + google.cloud.dialogflow_v2.services.session_entity_types.pagers.ListSessionEntityTypesAsyncPager: The response message for [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.v2.SessionEntityTypes.ListSessionEntityTypes]. @@ -257,7 +287,7 @@ async def get_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.GetSessionEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2.types.GetSessionEntityTypeRequest`): The request object. The request message for [SessionEntityTypes.GetSessionEntityType][google.cloud.dialogflow.v2.SessionEntityTypes.GetSessionEntityType]. name (:class:`str`): @@ -268,6 +298,7 @@ async def get_session_entity_type( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -279,17 +310,17 @@ async def get_session_entity_type( sent along with the request as metadata. Returns: - ~.session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -349,7 +380,7 @@ async def create_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.gcd_session_entity_type.CreateSessionEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2.types.CreateSessionEntityTypeRequest`): The request object. The request message for [SessionEntityTypes.CreateSessionEntityType][google.cloud.dialogflow.v2.SessionEntityTypes.CreateSessionEntityType]. parent (:class:`str`): @@ -360,12 +391,14 @@ async def create_session_entity_type( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - session_entity_type (:class:`~.gcd_session_entity_type.SessionEntityType`): + session_entity_type (:class:`google.cloud.dialogflow_v2.types.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. @@ -377,17 +410,17 @@ async def create_session_entity_type( sent along with the request as metadata. Returns: - ~.gcd_session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -446,18 +479,20 @@ async def update_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.gcd_session_entity_type.UpdateSessionEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2.types.UpdateSessionEntityTypeRequest`): The request object. The request message for [SessionEntityTypes.UpdateSessionEntityType][google.cloud.dialogflow.v2.SessionEntityTypes.UpdateSessionEntityType]. - session_entity_type (:class:`~.gcd_session_entity_type.SessionEntityType`): + session_entity_type (:class:`google.cloud.dialogflow_v2.types.SessionEntityType`): Required. The session entity type to update. + 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`): + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): Optional. 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. @@ -469,17 +504,17 @@ async def update_session_entity_type( sent along with the request as metadata. Returns: - ~.gcd_session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -539,7 +574,7 @@ async def delete_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.DeleteSessionEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2.types.DeleteSessionEntityTypeRequest`): The request object. The request message for [SessionEntityTypes.DeleteSessionEntityType][google.cloud.dialogflow.v2.SessionEntityTypes.DeleteSessionEntityType]. name (:class:`str`): @@ -550,6 +585,7 @@ async def delete_session_entity_type( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. diff --git a/google/cloud/dialogflow_v2/services/session_entity_types/client.py b/google/cloud/dialogflow_v2/services/session_entity_types/client.py index e85237b7b..01c08eba2 100644 --- a/google/cloud/dialogflow_v2/services/session_entity_types/client.py +++ b/google/cloud/dialogflow_v2/services/session_entity_types/client.py @@ -119,6 +119,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionEntityTypesClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -131,7 +147,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + SessionEntityTypesClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -239,10 +255,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.SessionEntityTypesTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -278,21 +294,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -335,7 +347,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -356,10 +368,10 @@ def list_session_entity_types( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + request (google.cloud.dialogflow_v2.types.ListSessionEntityTypesRequest): The request object. The request message for [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.v2.SessionEntityTypes.ListSessionEntityTypes]. - parent (:class:`str`): + parent (str): Required. The session to list all session entity types from. Format: ``projects//agent/sessions/`` or @@ -367,6 +379,7 @@ def list_session_entity_types( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -378,7 +391,7 @@ def list_session_entity_types( sent along with the request as metadata. Returns: - ~.pagers.ListSessionEntityTypesPager: + google.cloud.dialogflow_v2.services.session_entity_types.pagers.ListSessionEntityTypesPager: The response message for [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.v2.SessionEntityTypes.ListSessionEntityTypes]. @@ -448,10 +461,10 @@ def get_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.GetSessionEntityTypeRequest`): + request (google.cloud.dialogflow_v2.types.GetSessionEntityTypeRequest): The request object. The request message for [SessionEntityTypes.GetSessionEntityType][google.cloud.dialogflow.v2.SessionEntityTypes.GetSessionEntityType]. - name (:class:`str`): + name (str): Required. The name of the session entity type. Format: ``projects//agent/sessions//entityTypes/`` or @@ -459,6 +472,7 @@ def get_session_entity_type( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -470,17 +484,17 @@ def get_session_entity_type( sent along with the request as metadata. Returns: - ~.session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -541,10 +555,10 @@ def create_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.gcd_session_entity_type.CreateSessionEntityTypeRequest`): + request (google.cloud.dialogflow_v2.types.CreateSessionEntityTypeRequest): The request object. The request message for [SessionEntityTypes.CreateSessionEntityType][google.cloud.dialogflow.v2.SessionEntityTypes.CreateSessionEntityType]. - parent (:class:`str`): + parent (str): Required. The session to create a session entity type for. Format: ``projects//agent/sessions/`` or @@ -552,12 +566,14 @@ def create_session_entity_type( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - session_entity_type (:class:`~.gcd_session_entity_type.SessionEntityType`): + session_entity_type (google.cloud.dialogflow_v2.types.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. @@ -569,17 +585,17 @@ def create_session_entity_type( sent along with the request as metadata. Returns: - ~.gcd_session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -643,18 +659,20 @@ def update_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.gcd_session_entity_type.UpdateSessionEntityTypeRequest`): + request (google.cloud.dialogflow_v2.types.UpdateSessionEntityTypeRequest): The request object. The request message for [SessionEntityTypes.UpdateSessionEntityType][google.cloud.dialogflow.v2.SessionEntityTypes.UpdateSessionEntityType]. - session_entity_type (:class:`~.gcd_session_entity_type.SessionEntityType`): + session_entity_type (google.cloud.dialogflow_v2.types.SessionEntityType): Required. The session entity type to update. + 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`): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. 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. @@ -666,17 +684,17 @@ def update_session_entity_type( sent along with the request as metadata. Returns: - ~.gcd_session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -741,10 +759,10 @@ def delete_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.DeleteSessionEntityTypeRequest`): + request (google.cloud.dialogflow_v2.types.DeleteSessionEntityTypeRequest): The request object. The request message for [SessionEntityTypes.DeleteSessionEntityType][google.cloud.dialogflow.v2.SessionEntityTypes.DeleteSessionEntityType]. - name (:class:`str`): + name (str): Required. The name of the entity type to delete. Format: ``projects//agent/sessions//entityTypes/`` or @@ -752,6 +770,7 @@ def delete_session_entity_type( If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. diff --git a/google/cloud/dialogflow_v2/services/session_entity_types/pagers.py b/google/cloud/dialogflow_v2/services/session_entity_types/pagers.py index 0a9a3c177..3242ab3e7 100644 --- a/google/cloud/dialogflow_v2/services/session_entity_types/pagers.py +++ b/google/cloud/dialogflow_v2/services/session_entity_types/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2.types import session_entity_type @@ -24,7 +33,7 @@ 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 + :class:`google.cloud.dialogflow_v2.types.ListSessionEntityTypesResponse` object, and provides an ``__iter__`` method to iterate through its ``session_entity_types`` field. @@ -33,7 +42,7 @@ class ListSessionEntityTypesPager: through the ``session_entity_types`` field on the corresponding responses. - All the usual :class:`~.session_entity_type.ListSessionEntityTypesResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.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. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + request (google.cloud.dialogflow_v2.types.ListSessionEntityTypesRequest): The initial request object. - response (:class:`~.session_entity_type.ListSessionEntityTypesResponse`): + response (google.cloud.dialogflow_v2.types.ListSessionEntityTypesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ 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 + :class:`google.cloud.dialogflow_v2.types.ListSessionEntityTypesResponse` object, and provides an ``__aiter__`` method to iterate through its ``session_entity_types`` field. @@ -95,7 +104,7 @@ class ListSessionEntityTypesAsyncPager: through the ``session_entity_types`` field on the corresponding responses. - All the usual :class:`~.session_entity_type.ListSessionEntityTypesResponse` + All the usual :class:`google.cloud.dialogflow_v2.types.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. """ @@ -115,9 +124,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + request (google.cloud.dialogflow_v2.types.ListSessionEntityTypesRequest): The initial request object. - response (:class:`~.session_entity_type.ListSessionEntityTypesResponse`): + response (google.cloud.dialogflow_v2.types.ListSessionEntityTypesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2/services/session_entity_types/transports/grpc.py b/google/cloud/dialogflow_v2/services/session_entity_types/transports/grpc.py index 41734dbf9..9c59bc921 100644 --- a/google/cloud/dialogflow_v2/services/session_entity_types/transports/grpc.py +++ b/google/cloud/dialogflow_v2/services/session_entity_types/transports/grpc.py @@ -62,6 +62,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -92,6 +93,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -108,6 +113,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -117,11 +127,6 @@ def __init__( 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 @@ -165,12 +170,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/session_entity_types/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/session_entity_types/transports/grpc_asyncio.py index 05aa89321..7f8d60544 100644 --- a/google/cloud/dialogflow_v2/services/session_entity_types/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2/services/session_entity_types/transports/grpc_asyncio.py @@ -106,6 +106,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -137,6 +138,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -153,6 +158,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -162,11 +172,6 @@ def __init__( 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 @@ -210,12 +215,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/sessions/async_client.py b/google/cloud/dialogflow_v2/services/sessions/async_client.py index 4e5f36b55..5107f62c4 100644 --- a/google/cloud/dialogflow_v2/services/sessions/async_client.py +++ b/google/cloud/dialogflow_v2/services/sessions/async_client.py @@ -91,7 +91,36 @@ class SessionsAsyncClient: 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 + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionsAsyncClient: The constructed client. + """ + return SessionsClient.from_service_account_info.__func__(SessionsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionsAsyncClient: The constructed client. + """ + return SessionsClient.from_service_account_file.__func__(SessionsAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -175,7 +204,7 @@ async def detect_intent( environments `__. Args: - request (:class:`~.gcd_session.DetectIntentRequest`): + request (:class:`google.cloud.dialogflow_v2.types.DetectIntentRequest`): The request object. The request to detect user's intent. session (:class:`str`): Required. The name of the session this query is sent to. @@ -184,13 +213,14 @@ async def detect_intent( or ``projects//agent/environments//users//sessions/``. If ``Environment ID`` is not specified, we assume - default 'draft' environment. If ``User ID`` is not - specified, we are using "-". It's up to the API caller - to choose an appropriate ``Session ID`` and ``User Id``. - They can be a random number or some type of user and - session identifiers (preferably hashed). The length of - the ``Session ID`` and ``User ID`` must not exceed 36 - characters. + default 'draft' environment (``Environment ID`` might be + referred to as environment name at some places). If + ``User ID`` is not specified, we are using "-". It's up + to the API caller to choose an appropriate + ``Session ID`` and ``User Id``. They can be a random + number or some type of user and session identifiers + (preferably hashed). The length of the ``Session ID`` + and ``User ID`` must not exceed 36 characters. For more information, see the `API interactions guide `__. @@ -198,10 +228,11 @@ async def detect_intent( Note: Always use agent versions for production traffic. See `Versions and environments `__. + This corresponds to the ``session`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - query_input (:class:`~.gcd_session.QueryInput`): + query_input (:class:`google.cloud.dialogflow_v2.types.QueryInput`): Required. The input specification. It can be set to: 1. an audio config @@ -212,6 +243,7 @@ async def detect_intent( of text, or 3. an event that specifies which intent to trigger. + This corresponds to the ``query_input`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -223,7 +255,7 @@ async def detect_intent( sent along with the request as metadata. Returns: - ~.gcd_session.DetectIntentResponse: + google.cloud.dialogflow_v2.types.DetectIntentResponse: The message returned from the DetectIntent method. @@ -292,12 +324,11 @@ def streaming_detect_intent( environments `__. Args: - requests (AsyncIterator[`~.session.StreamingDetectIntentRequest`]): + requests (AsyncIterator[`google.cloud.dialogflow_v2.types.StreamingDetectIntentRequest`]): The request object AsyncIterator. The top-level message sent by the client to the [Sessions.StreamingDetectIntent][google.cloud.dialogflow.v2.Sessions.StreamingDetectIntent] method. - Multiple request messages should be sent in order: 1. The first message must contain @@ -305,7 +336,7 @@ def streaming_detect_intent( [query_input][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.query_input] plus optionally [query_params][google.cloud.dialogflow.v2.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.v2.StreamingDetectIntentRequest.output_audio_config]. The message must not contain @@ -314,12 +345,12 @@ def streaming_detect_intent( [query_input][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.query_input] was set to [query_input.audio_config][google.cloud.dialogflow.v2.InputAudioConfig], - all subsequent messages must contain + all subsequent messages must contain [input_audio][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.input_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.v2.QueryInput.text]. However, note that: @@ -337,22 +368,20 @@ def streaming_detect_intent( sent along with the request as metadata. Returns: - AsyncIterable[~.session.StreamingDetectIntentResponse]: + AsyncIterable[google.cloud.dialogflow_v2.types.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 next message contains ``response_id``, - ``query_result`` and optionally ``webhook_status`` if - a WebHook was called. + 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 next message contains response_id, + query_result and optionally webhook_status if a + WebHook was called. """ diff --git a/google/cloud/dialogflow_v2/services/sessions/client.py b/google/cloud/dialogflow_v2/services/sessions/client.py index e19cd5989..f601843e0 100644 --- a/google/cloud/dialogflow_v2/services/sessions/client.py +++ b/google/cloud/dialogflow_v2/services/sessions/client.py @@ -124,6 +124,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -136,7 +152,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + SessionsClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -288,10 +304,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.SessionsTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -327,21 +343,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -384,7 +396,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -409,22 +421,23 @@ def detect_intent( environments `__. Args: - request (:class:`~.gcd_session.DetectIntentRequest`): + request (google.cloud.dialogflow_v2.types.DetectIntentRequest): The request object. The request to detect user's intent. - session (:class:`str`): + session (str): Required. The name of the session this query is sent to. Format: ``projects//agent/sessions/``, or ``projects//agent/environments//users//sessions/``. If ``Environment ID`` is not specified, we assume - default 'draft' environment. If ``User ID`` is not - specified, we are using "-". It's up to the API caller - to choose an appropriate ``Session ID`` and ``User Id``. - They can be a random number or some type of user and - session identifiers (preferably hashed). The length of - the ``Session ID`` and ``User ID`` must not exceed 36 - characters. + default 'draft' environment (``Environment ID`` might be + referred to as environment name at some places). If + ``User ID`` is not specified, we are using "-". It's up + to the API caller to choose an appropriate + ``Session ID`` and ``User Id``. They can be a random + number or some type of user and session identifiers + (preferably hashed). The length of the ``Session ID`` + and ``User ID`` must not exceed 36 characters. For more information, see the `API interactions guide `__. @@ -432,10 +445,11 @@ def detect_intent( Note: Always use agent versions for production traffic. See `Versions and environments `__. + This corresponds to the ``session`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - query_input (:class:`~.gcd_session.QueryInput`): + query_input (google.cloud.dialogflow_v2.types.QueryInput): Required. The input specification. It can be set to: 1. an audio config @@ -446,6 +460,7 @@ def detect_intent( of text, or 3. an event that specifies which intent to trigger. + This corresponds to the ``query_input`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -457,7 +472,7 @@ def detect_intent( sent along with the request as metadata. Returns: - ~.gcd_session.DetectIntentResponse: + google.cloud.dialogflow_v2.types.DetectIntentResponse: The message returned from the DetectIntent method. @@ -521,12 +536,11 @@ def streaming_detect_intent( environments `__. Args: - requests (Iterator[`~.session.StreamingDetectIntentRequest`]): + requests (Iterator[google.cloud.dialogflow_v2.types.StreamingDetectIntentRequest]): The request object iterator. The top-level message sent by the client to the [Sessions.StreamingDetectIntent][google.cloud.dialogflow.v2.Sessions.StreamingDetectIntent] method. - Multiple request messages should be sent in order: 1. The first message must contain @@ -534,7 +548,7 @@ def streaming_detect_intent( [query_input][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.query_input] plus optionally [query_params][google.cloud.dialogflow.v2.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.v2.StreamingDetectIntentRequest.output_audio_config]. The message must not contain @@ -543,12 +557,12 @@ def streaming_detect_intent( [query_input][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.query_input] was set to [query_input.audio_config][google.cloud.dialogflow.v2.InputAudioConfig], - all subsequent messages must contain + all subsequent messages must contain [input_audio][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.input_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.v2.QueryInput.text]. However, note that: @@ -566,22 +580,20 @@ def streaming_detect_intent( sent along with the request as metadata. Returns: - Iterable[~.session.StreamingDetectIntentResponse]: + Iterable[google.cloud.dialogflow_v2.types.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 next message contains ``response_id``, - ``query_result`` and optionally ``webhook_status`` if - a WebHook was called. + 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 next message contains response_id, + query_result and optionally webhook_status if a + WebHook was called. """ diff --git a/google/cloud/dialogflow_v2/services/sessions/transports/grpc.py b/google/cloud/dialogflow_v2/services/sessions/transports/grpc.py index 894d76d3e..a1453406e 100644 --- a/google/cloud/dialogflow_v2/services/sessions/transports/grpc.py +++ b/google/cloud/dialogflow_v2/services/sessions/transports/grpc.py @@ -61,6 +61,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -91,6 +92,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -107,6 +112,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -116,11 +126,6 @@ def __init__( 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 @@ -164,12 +169,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/services/sessions/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2/services/sessions/transports/grpc_asyncio.py index 6973e97c0..fe8954714 100644 --- a/google/cloud/dialogflow_v2/services/sessions/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2/services/sessions/transports/grpc_asyncio.py @@ -105,6 +105,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -136,6 +137,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -152,6 +157,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -161,11 +171,6 @@ def __init__( 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 @@ -209,12 +214,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2/types/__init__.py b/google/cloud/dialogflow_v2/types/__init__.py index b3fcfd7c7..8b850081c 100644 --- a/google/cloud/dialogflow_v2/types/__init__.py +++ b/google/cloud/dialogflow_v2/types/__init__.py @@ -15,194 +15,366 @@ # limitations under the License. # -from .validation_result import ( - ValidationError, - ValidationResult, -) from .agent import ( Agent, - GetAgentRequest, - SetAgentRequest, DeleteAgentRequest, - SearchAgentsRequest, - SearchAgentsResponse, - TrainAgentRequest, ExportAgentRequest, ExportAgentResponse, + GetAgentRequest, + GetValidationResultRequest, ImportAgentRequest, RestoreAgentRequest, - GetValidationResultRequest, + SearchAgentsRequest, + SearchAgentsResponse, + SetAgentRequest, + TrainAgentRequest, +) +from .answer_record import ( + AgentAssistantFeedback, + AgentAssistantRecord, + AnswerFeedback, + AnswerRecord, + ListAnswerRecordsRequest, + ListAnswerRecordsResponse, + UpdateAnswerRecordRequest, ) from .audio_config import ( + InputAudioConfig, + OutputAudioConfig, SpeechContext, + SpeechToTextConfig, SpeechWordInfo, - InputAudioConfig, - VoiceSelectionParams, SynthesizeSpeechConfig, - OutputAudioConfig, + TelephonyDtmfEvents, + VoiceSelectionParams, AudioEncoding, + OutputAudioEncoding, SpeechModelVariant, SsmlVoiceGender, - OutputAudioEncoding, + TelephonyDtmf, ) from .context import ( Context, + CreateContextRequest, + DeleteAllContextsRequest, + DeleteContextRequest, + GetContextRequest, ListContextsRequest, ListContextsResponse, - GetContextRequest, - CreateContextRequest, UpdateContextRequest, - DeleteContextRequest, - DeleteAllContextsRequest, +) +from .conversation import ( + CallMatcher, + CompleteConversationRequest, + Conversation, + ConversationPhoneNumber, + CreateCallMatcherRequest, + CreateConversationRequest, + DeleteCallMatcherRequest, + GetConversationRequest, + ListCallMatchersRequest, + ListCallMatchersResponse, + ListConversationsRequest, + ListConversationsResponse, + ListMessagesRequest, + ListMessagesResponse, +) +from .conversation_event import ConversationEvent +from .conversation_profile import ( + AutomatedAgentConfig, + ConversationProfile, + CreateConversationProfileRequest, + DeleteConversationProfileRequest, + GetConversationProfileRequest, + HumanAgentAssistantConfig, + HumanAgentHandoffConfig, + ListConversationProfilesRequest, + ListConversationProfilesResponse, + LoggingConfig, + NotificationConfig, + SuggestionFeature, + UpdateConversationProfileRequest, +) +from .document import ( + CreateDocumentRequest, + DeleteDocumentRequest, + Document, + GetDocumentRequest, + KnowledgeOperationMetadata, + ListDocumentsRequest, + ListDocumentsResponse, + ReloadDocumentRequest, + UpdateDocumentRequest, ) from .entity_type import ( + BatchCreateEntitiesRequest, + BatchDeleteEntitiesRequest, + BatchDeleteEntityTypesRequest, + BatchUpdateEntitiesRequest, + BatchUpdateEntityTypesRequest, + BatchUpdateEntityTypesResponse, + CreateEntityTypeRequest, + DeleteEntityTypeRequest, EntityType, + EntityTypeBatch, + GetEntityTypeRequest, ListEntityTypesRequest, ListEntityTypesResponse, - GetEntityTypeRequest, - CreateEntityTypeRequest, UpdateEntityTypeRequest, - DeleteEntityTypeRequest, - BatchUpdateEntityTypesRequest, - BatchUpdateEntityTypesResponse, - BatchDeleteEntityTypesRequest, - BatchCreateEntitiesRequest, - BatchUpdateEntitiesRequest, - BatchDeleteEntitiesRequest, - EntityTypeBatch, ) from .environment import ( Environment, ListEnvironmentsRequest, ListEnvironmentsResponse, ) +from .human_agent_assistant_event import HumanAgentAssistantEvent from .intent import ( + BatchDeleteIntentsRequest, + BatchUpdateIntentsRequest, + BatchUpdateIntentsResponse, + CreateIntentRequest, + DeleteIntentRequest, + GetIntentRequest, Intent, + IntentBatch, ListIntentsRequest, ListIntentsResponse, - GetIntentRequest, - CreateIntentRequest, UpdateIntentRequest, - DeleteIntentRequest, - BatchUpdateIntentsRequest, - BatchUpdateIntentsResponse, - BatchDeleteIntentsRequest, - IntentBatch, IntentView, ) -from .session_entity_type import ( - SessionEntityType, - ListSessionEntityTypesRequest, - ListSessionEntityTypesResponse, - GetSessionEntityTypeRequest, - CreateSessionEntityTypeRequest, - UpdateSessionEntityTypeRequest, - DeleteSessionEntityTypeRequest, +from .knowledge_base import ( + CreateKnowledgeBaseRequest, + DeleteKnowledgeBaseRequest, + GetKnowledgeBaseRequest, + KnowledgeBase, + ListKnowledgeBasesRequest, + ListKnowledgeBasesResponse, + UpdateKnowledgeBaseRequest, +) +from .participant import ( + AnalyzeContentRequest, + AnalyzeContentResponse, + AnnotatedMessagePart, + ArticleAnswer, + AudioInput, + AutomatedAgentReply, + CreateParticipantRequest, + DtmfParameters, + FaqAnswer, + GetParticipantRequest, + InputTextConfig, + ListParticipantsRequest, + ListParticipantsResponse, + Message, + MessageAnnotation, + OutputAudio, + Participant, + StreamingAnalyzeContentRequest, + StreamingAnalyzeContentResponse, + SuggestArticlesRequest, + SuggestArticlesResponse, + SuggestFaqAnswersRequest, + SuggestFaqAnswersResponse, + SuggestionResult, + UpdateParticipantRequest, ) from .session import ( DetectIntentRequest, DetectIntentResponse, - QueryParameters, + EventInput, QueryInput, + QueryParameters, QueryResult, + Sentiment, + SentimentAnalysisRequestConfig, + SentimentAnalysisResult, StreamingDetectIntentRequest, StreamingDetectIntentResponse, StreamingRecognitionResult, TextInput, - EventInput, - SentimentAnalysisRequestConfig, - SentimentAnalysisResult, - Sentiment, +) +from .session_entity_type import ( + CreateSessionEntityTypeRequest, + DeleteSessionEntityTypeRequest, + GetSessionEntityTypeRequest, + ListSessionEntityTypesRequest, + ListSessionEntityTypesResponse, + SessionEntityType, + UpdateSessionEntityTypeRequest, +) +from .validation_result import ( + ValidationError, + ValidationResult, ) from .webhook import ( + OriginalDetectIntentRequest, WebhookRequest, WebhookResponse, - OriginalDetectIntentRequest, ) __all__ = ( - "ValidationError", - "ValidationResult", "Agent", - "GetAgentRequest", - "SetAgentRequest", "DeleteAgentRequest", - "SearchAgentsRequest", - "SearchAgentsResponse", - "TrainAgentRequest", "ExportAgentRequest", "ExportAgentResponse", + "GetAgentRequest", + "GetValidationResultRequest", "ImportAgentRequest", "RestoreAgentRequest", - "GetValidationResultRequest", + "SearchAgentsRequest", + "SearchAgentsResponse", + "SetAgentRequest", + "TrainAgentRequest", + "AgentAssistantFeedback", + "AgentAssistantRecord", + "AnswerFeedback", + "AnswerRecord", + "ListAnswerRecordsRequest", + "ListAnswerRecordsResponse", + "UpdateAnswerRecordRequest", + "InputAudioConfig", + "OutputAudioConfig", "SpeechContext", + "SpeechToTextConfig", "SpeechWordInfo", - "InputAudioConfig", - "VoiceSelectionParams", "SynthesizeSpeechConfig", - "OutputAudioConfig", + "TelephonyDtmfEvents", + "VoiceSelectionParams", "AudioEncoding", + "OutputAudioEncoding", "SpeechModelVariant", "SsmlVoiceGender", - "OutputAudioEncoding", + "TelephonyDtmf", "Context", + "CreateContextRequest", + "DeleteAllContextsRequest", + "DeleteContextRequest", + "GetContextRequest", "ListContextsRequest", "ListContextsResponse", - "GetContextRequest", - "CreateContextRequest", "UpdateContextRequest", - "DeleteContextRequest", - "DeleteAllContextsRequest", + "CallMatcher", + "CompleteConversationRequest", + "Conversation", + "ConversationPhoneNumber", + "CreateCallMatcherRequest", + "CreateConversationRequest", + "DeleteCallMatcherRequest", + "GetConversationRequest", + "ListCallMatchersRequest", + "ListCallMatchersResponse", + "ListConversationsRequest", + "ListConversationsResponse", + "ListMessagesRequest", + "ListMessagesResponse", + "ConversationEvent", + "AutomatedAgentConfig", + "ConversationProfile", + "CreateConversationProfileRequest", + "DeleteConversationProfileRequest", + "GetConversationProfileRequest", + "HumanAgentAssistantConfig", + "HumanAgentHandoffConfig", + "ListConversationProfilesRequest", + "ListConversationProfilesResponse", + "LoggingConfig", + "NotificationConfig", + "SuggestionFeature", + "UpdateConversationProfileRequest", + "CreateDocumentRequest", + "DeleteDocumentRequest", + "Document", + "GetDocumentRequest", + "KnowledgeOperationMetadata", + "ListDocumentsRequest", + "ListDocumentsResponse", + "ReloadDocumentRequest", + "UpdateDocumentRequest", + "BatchCreateEntitiesRequest", + "BatchDeleteEntitiesRequest", + "BatchDeleteEntityTypesRequest", + "BatchUpdateEntitiesRequest", + "BatchUpdateEntityTypesRequest", + "BatchUpdateEntityTypesResponse", + "CreateEntityTypeRequest", + "DeleteEntityTypeRequest", "EntityType", + "EntityTypeBatch", + "GetEntityTypeRequest", "ListEntityTypesRequest", "ListEntityTypesResponse", - "GetEntityTypeRequest", - "CreateEntityTypeRequest", "UpdateEntityTypeRequest", - "DeleteEntityTypeRequest", - "BatchUpdateEntityTypesRequest", - "BatchUpdateEntityTypesResponse", - "BatchDeleteEntityTypesRequest", - "BatchCreateEntitiesRequest", - "BatchUpdateEntitiesRequest", - "BatchDeleteEntitiesRequest", - "EntityTypeBatch", "Environment", "ListEnvironmentsRequest", "ListEnvironmentsResponse", + "HumanAgentAssistantEvent", + "BatchDeleteIntentsRequest", + "BatchUpdateIntentsRequest", + "BatchUpdateIntentsResponse", + "CreateIntentRequest", + "DeleteIntentRequest", + "GetIntentRequest", "Intent", + "IntentBatch", "ListIntentsRequest", "ListIntentsResponse", - "GetIntentRequest", - "CreateIntentRequest", "UpdateIntentRequest", - "DeleteIntentRequest", - "BatchUpdateIntentsRequest", - "BatchUpdateIntentsResponse", - "BatchDeleteIntentsRequest", - "IntentBatch", "IntentView", - "SessionEntityType", - "ListSessionEntityTypesRequest", - "ListSessionEntityTypesResponse", - "GetSessionEntityTypeRequest", - "CreateSessionEntityTypeRequest", - "UpdateSessionEntityTypeRequest", - "DeleteSessionEntityTypeRequest", + "CreateKnowledgeBaseRequest", + "DeleteKnowledgeBaseRequest", + "GetKnowledgeBaseRequest", + "KnowledgeBase", + "ListKnowledgeBasesRequest", + "ListKnowledgeBasesResponse", + "UpdateKnowledgeBaseRequest", + "AnalyzeContentRequest", + "AnalyzeContentResponse", + "AnnotatedMessagePart", + "ArticleAnswer", + "AudioInput", + "AutomatedAgentReply", + "CreateParticipantRequest", + "DtmfParameters", + "FaqAnswer", + "GetParticipantRequest", + "InputTextConfig", + "ListParticipantsRequest", + "ListParticipantsResponse", + "Message", + "MessageAnnotation", + "OutputAudio", + "Participant", + "StreamingAnalyzeContentRequest", + "StreamingAnalyzeContentResponse", + "SuggestArticlesRequest", + "SuggestArticlesResponse", + "SuggestFaqAnswersRequest", + "SuggestFaqAnswersResponse", + "SuggestionResult", + "UpdateParticipantRequest", "DetectIntentRequest", "DetectIntentResponse", - "QueryParameters", + "EventInput", "QueryInput", + "QueryParameters", "QueryResult", + "Sentiment", + "SentimentAnalysisRequestConfig", + "SentimentAnalysisResult", "StreamingDetectIntentRequest", "StreamingDetectIntentResponse", "StreamingRecognitionResult", "TextInput", - "EventInput", - "SentimentAnalysisRequestConfig", - "SentimentAnalysisResult", - "Sentiment", + "CreateSessionEntityTypeRequest", + "DeleteSessionEntityTypeRequest", + "GetSessionEntityTypeRequest", + "ListSessionEntityTypesRequest", + "ListSessionEntityTypesResponse", + "SessionEntityType", + "UpdateSessionEntityTypeRequest", + "ValidationError", + "ValidationResult", + "OriginalDetectIntentRequest", "WebhookRequest", "WebhookResponse", - "OriginalDetectIntentRequest", ) diff --git a/google/cloud/dialogflow_v2/types/agent.py b/google/cloud/dialogflow_v2/types/agent.py index 721c5dc90..9309c84bc 100644 --- a/google/cloud/dialogflow_v2/types/agent.py +++ b/google/cloud/dialogflow_v2/types/agent.py @@ -84,7 +84,7 @@ class Agent(proto.Message): enable_logging (bool): Optional. Determines whether this agent should log conversation queries. - match_mode (~.gcd_agent.Agent.MatchMode): + match_mode (google.cloud.dialogflow_v2.types.Agent.MatchMode): Optional. Determines how intents are detected from user queries. classification_threshold (float): @@ -99,14 +99,14 @@ class Agent(proto.Message): values range from 0.0 (completely uncertain) to 1.0 (completely certain). If set to 0.0, the default of 0.3 is used. - api_version (~.gcd_agent.Agent.ApiVersion): + api_version (google.cloud.dialogflow_v2.types.Agent.ApiVersion): Optional. API version displayed in Dialogflow console. If not specified, V2 API is assumed. Clients are free to query different service endpoints for different API versions. However, bots connectors and webhook calls will follow the specified API version. - tier (~.gcd_agent.Agent.Tier): + tier (google.cloud.dialogflow_v2.types.Agent.Tier): Optional. The agent tier. If not specified, TIER_STANDARD is assumed. """ @@ -176,9 +176,9 @@ class SetAgentRequest(proto.Message): [Agents.SetAgent][google.cloud.dialogflow.v2.Agents.SetAgent]. Attributes: - agent (~.gcd_agent.Agent): + agent (google.cloud.dialogflow_v2.types.Agent): Required. The agent to update. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ @@ -230,7 +230,7 @@ class SearchAgentsResponse(proto.Message): [Agents.SearchAgents][google.cloud.dialogflow.v2.Agents.SearchAgents]. Attributes: - agents (Sequence[~.gcd_agent.Agent]): + agents (Sequence[google.cloud.dialogflow_v2.types.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): diff --git a/google/cloud/dialogflow_v2/types/answer_record.py b/google/cloud/dialogflow_v2/types/answer_record.py new file mode 100644 index 000000000..57866e0e5 --- /dev/null +++ b/google/cloud/dialogflow_v2/types/answer_record.py @@ -0,0 +1,301 @@ +# -*- 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.dialogflow_v2.types import participant +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.v2", + manifest={ + "AnswerRecord", + "ListAnswerRecordsRequest", + "ListAnswerRecordsResponse", + "UpdateAnswerRecordRequest", + "AnswerFeedback", + "AgentAssistantFeedback", + "AgentAssistantRecord", + }, +) + + +class AnswerRecord(proto.Message): + r"""Answer records are records to manage answer history and feedbacks + for Dialogflow. + + Currently, answer record includes: + + - human agent assistant article suggestion + - human agent assistant faq article + + It doesn't include: + + - ``DetectIntent`` intent matching + - ``DetectIntent`` knowledge + + Answer records are not related to the conversation history in the + Dialogflow Console. A Record is generated even when the end-user + disables conversation history in the console. Records are created + when there's a human agent assistant suggestion generated. + + A typical workflow for customers provide feedback to an answer is: + + 1. For human agent assistant, customers get suggestion via + ListSuggestions API. Together with the answers, + [AnswerRecord.name][google.cloud.dialogflow.v2.AnswerRecord.name] + are returned to the customers. + 2. The customer uses the + [AnswerRecord.name][google.cloud.dialogflow.v2.AnswerRecord.name] + to call the [UpdateAnswerRecord][] method to send feedback about + a specific answer that they believe is wrong. + + Attributes: + name (str): + The unique identifier of this answer record. Format: + ``projects//locations//answerRecords/``. + answer_feedback (google.cloud.dialogflow_v2.types.AnswerFeedback): + Required. The AnswerFeedback for this record. You can set + this with + [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2.AnswerRecords.UpdateAnswerRecord] + in order to give us feedback about this answer. + agent_assistant_record (google.cloud.dialogflow_v2.types.AgentAssistantRecord): + Output only. The record for human agent + assistant. + """ + + name = proto.Field(proto.STRING, number=1) + + answer_feedback = proto.Field(proto.MESSAGE, number=2, message="AnswerFeedback",) + + agent_assistant_record = proto.Field( + proto.MESSAGE, number=4, oneof="record", message="AgentAssistantRecord", + ) + + +class ListAnswerRecordsRequest(proto.Message): + r"""Request message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2.AnswerRecords.ListAnswerRecords]. + + Attributes: + parent (str): + Required. The project to list all answer records for in + reverse chronological order. Format: + ``projects//locations/``. + filter (str): + Required. Filters to restrict results to specific answer + records. Filter on answer record type. Currently predicates + on ``type`` is supported, valid values are + ``ARTICLE_ANSWER``, ``FAQ_ANSWER``. + + For more information about filtering, see `API + Filtering `__. + page_size (int): + Optional. The maximum number of records to + return in a single page. The server may return + fewer records than this. If unspecified, we use + 10. The maximum is 100. + page_token (str): + Optional. The + [ListAnswerRecordsResponse.next_page_token][google.cloud.dialogflow.v2.ListAnswerRecordsResponse.next_page_token] + value returned from a previous list request used to continue + listing on the next page. + """ + + parent = proto.Field(proto.STRING, number=1) + + filter = proto.Field(proto.STRING, number=2) + + page_size = proto.Field(proto.INT32, number=3) + + page_token = proto.Field(proto.STRING, number=4) + + +class ListAnswerRecordsResponse(proto.Message): + r"""Response message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2.AnswerRecords.ListAnswerRecords]. + + Attributes: + answer_records (Sequence[google.cloud.dialogflow_v2.types.AnswerRecord]): + The list of answer records. + next_page_token (str): + A token to retrieve next page of results. Or empty if there + are no more results. Pass this value in the + [ListAnswerRecordsRequest.page_token][google.cloud.dialogflow.v2.ListAnswerRecordsRequest.page_token] + field in the subsequent call to ``ListAnswerRecords`` method + to retrieve the next page of results. + """ + + @property + def raw_page(self): + return self + + answer_records = proto.RepeatedField( + proto.MESSAGE, number=1, message="AnswerRecord", + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class UpdateAnswerRecordRequest(proto.Message): + r"""Request message for + [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2.AnswerRecords.UpdateAnswerRecord]. + + Attributes: + answer_record (google.cloud.dialogflow_v2.types.AnswerRecord): + Required. Answer record to update. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. The mask to control which fields + get updated. + """ + + answer_record = proto.Field(proto.MESSAGE, number=1, message="AnswerRecord",) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class AnswerFeedback(proto.Message): + r"""Represents feedback the customer has about the quality & + correctness of a certain answer in a conversation. + + Attributes: + correctness_level (google.cloud.dialogflow_v2.types.AnswerFeedback.CorrectnessLevel): + The correctness level of the specific answer. + agent_assistant_detail_feedback (google.cloud.dialogflow_v2.types.AgentAssistantFeedback): + Detail feedback of agent assist suggestions. + clicked (bool): + Indicates whether the answer/item was clicked + by the human agent or not. Default to false. + click_time (google.protobuf.timestamp_pb2.Timestamp): + Time when the answer/item was clicked. + displayed (bool): + Indicates whether the answer/item was + displayed to the human agent in the agent + desktop UI. Default to false. + display_time (google.protobuf.timestamp_pb2.Timestamp): + Time when the answer/item was displayed. + """ + + class CorrectnessLevel(proto.Enum): + r"""The correctness level of an answer.""" + CORRECTNESS_LEVEL_UNSPECIFIED = 0 + NOT_CORRECT = 1 + PARTIALLY_CORRECT = 2 + FULLY_CORRECT = 3 + + correctness_level = proto.Field(proto.ENUM, number=1, enum=CorrectnessLevel,) + + agent_assistant_detail_feedback = proto.Field( + proto.MESSAGE, + number=2, + oneof="detail_feedback", + message="AgentAssistantFeedback", + ) + + clicked = proto.Field(proto.BOOL, number=3) + + click_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) + + displayed = proto.Field(proto.BOOL, number=4) + + display_time = proto.Field(proto.MESSAGE, number=6, message=timestamp.Timestamp,) + + +class AgentAssistantFeedback(proto.Message): + r"""Detail feedback of Agent Assist result. + + Attributes: + answer_relevance (google.cloud.dialogflow_v2.types.AgentAssistantFeedback.AnswerRelevance): + Optional. Whether or not the suggested answer is relevant. + + For example: + + - Query: "Can I change my mailing address?" + - Suggested document says: "Items must be + returned/exchanged within 60 days of the purchase date." + - [answer_relevance][google.cloud.dialogflow.v2.AgentAssistantFeedback.answer_relevance]: + [AnswerRelevance.IRRELEVANT][google.cloud.dialogflow.v2.AgentAssistantFeedback.AnswerRelevance.IRRELEVANT] + document_correctness (google.cloud.dialogflow_v2.types.AgentAssistantFeedback.DocumentCorrectness): + Optional. Whether or not the information in the document is + correct. + + For example: + + - Query: "Can I return the package in 2 days once + received?" + - Suggested document says: "Items must be + returned/exchanged within 60 days of the purchase date." + - Ground truth: "No return or exchange is allowed." + - + document_efficiency (google.cloud.dialogflow_v2.types.AgentAssistantFeedback.DocumentEfficiency): + Optional. Whether or not the suggested document is + efficient. For example, if the document is poorly written, + hard to understand, hard to use or too long to find useful + information, + [document_efficiency][google.cloud.dialogflow.v2.AgentAssistantFeedback.document_efficiency] + is + [DocumentEfficiency.INEFFICIENT][google.cloud.dialogflow.v2.AgentAssistantFeedback.DocumentEfficiency.INEFFICIENT]. + """ + + class AnswerRelevance(proto.Enum): + r"""Relevance of an answer.""" + ANSWER_RELEVANCE_UNSPECIFIED = 0 + IRRELEVANT = 1 + RELEVANT = 2 + + class DocumentCorrectness(proto.Enum): + r"""Correctness of document.""" + DOCUMENT_CORRECTNESS_UNSPECIFIED = 0 + INCORRECT = 1 + CORRECT = 2 + + class DocumentEfficiency(proto.Enum): + r"""Efficiency of document.""" + DOCUMENT_EFFICIENCY_UNSPECIFIED = 0 + INEFFICIENT = 1 + EFFICIENT = 2 + + answer_relevance = proto.Field(proto.ENUM, number=1, enum=AnswerRelevance,) + + document_correctness = proto.Field(proto.ENUM, number=2, enum=DocumentCorrectness,) + + document_efficiency = proto.Field(proto.ENUM, number=3, enum=DocumentEfficiency,) + + +class AgentAssistantRecord(proto.Message): + r"""Represents a record of a human agent assist answer. + + Attributes: + article_suggestion_answer (google.cloud.dialogflow_v2.types.ArticleAnswer): + Output only. The article suggestion answer. + faq_answer (google.cloud.dialogflow_v2.types.FaqAnswer): + Output only. The FAQ answer. + """ + + article_suggestion_answer = proto.Field( + proto.MESSAGE, number=5, oneof="answer", message=participant.ArticleAnswer, + ) + + faq_answer = proto.Field( + proto.MESSAGE, number=6, oneof="answer", message=participant.FaqAnswer, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2/types/audio_config.py b/google/cloud/dialogflow_v2/types/audio_config.py index 1ecf86bed..83d724e1d 100644 --- a/google/cloud/dialogflow_v2/types/audio_config.py +++ b/google/cloud/dialogflow_v2/types/audio_config.py @@ -28,12 +28,15 @@ "SpeechModelVariant", "SsmlVoiceGender", "OutputAudioEncoding", + "TelephonyDtmf", "SpeechContext", "SpeechWordInfo", "InputAudioConfig", "VoiceSelectionParams", "SynthesizeSpeechConfig", "OutputAudioConfig", + "TelephonyDtmfEvents", + "SpeechToTextConfig", }, ) @@ -89,6 +92,29 @@ class OutputAudioEncoding(proto.Enum): OUTPUT_AUDIO_ENCODING_OGG_OPUS = 3 +class TelephonyDtmf(proto.Enum): + r"""`DTMF `__ + digit in Telephony Gateway. + """ + TELEPHONY_DTMF_UNSPECIFIED = 0 + DTMF_ONE = 1 + DTMF_TWO = 2 + DTMF_THREE = 3 + DTMF_FOUR = 4 + DTMF_FIVE = 5 + DTMF_SIX = 6 + DTMF_SEVEN = 7 + DTMF_EIGHT = 8 + DTMF_NINE = 9 + DTMF_ZERO = 10 + DTMF_A = 11 + DTMF_B = 12 + DTMF_C = 13 + DTMF_D = 14 + DTMF_STAR = 15 + DTMF_POUND = 16 + + class SpeechContext(proto.Message): r"""Hints for the speech recognizer to help with recognition in a specific conversation state. @@ -135,12 +161,12 @@ class SpeechWordInfo(proto.Message): Attributes: word (str): The word this info is for. - start_offset (~.duration.Duration): + start_offset (google.protobuf.duration_pb2.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): + end_offset (google.protobuf.duration_pb2.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 @@ -172,7 +198,7 @@ class InputAudioConfig(proto.Message): content. Attributes: - audio_encoding (~.audio_config.AudioEncoding): + audio_encoding (google.cloud.dialogflow_v2.types.AudioEncoding): Required. Audio encoding of the audio content to process. sample_rate_hertz (int): @@ -208,7 +234,7 @@ class InputAudioConfig(proto.Message): `speech_contexts <>`__, Dialogflow will treat the `phrase_hints <>`__ as a single additional `SpeechContext <>`__. - speech_contexts (Sequence[~.audio_config.SpeechContext]): + speech_contexts (Sequence[google.cloud.dialogflow_v2.types.SpeechContext]): Context information to assist speech recognition. See `the Cloud Speech @@ -226,7 +252,7 @@ class InputAudioConfig(proto.Message): Speech API documentation `__ for more details. - model_variant (~.audio_config.SpeechModelVariant): + model_variant (google.cloud.dialogflow_v2.types.SpeechModelVariant): Which variant of the [Speech model][google.cloud.dialogflow.v2.InputAudioConfig.model] to use. @@ -241,6 +267,13 @@ class InputAudioConfig(proto.Message): only for streaming methods. Note: When specified, InputAudioConfig.single_utterance takes precedence over StreamingDetectIntentRequest.single_utterance. + disable_no_speech_recognized_event (bool): + Only used in + [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent] + and + [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2.Participants.StreamingAnalyzeContent]. + If ``false`` and recognition doesn't return any result, + trigger ``NO_SPEECH_RECOGNIZED`` event to Dialogflow agent. """ audio_encoding = proto.Field(proto.ENUM, number=1, enum="AudioEncoding",) @@ -263,6 +296,8 @@ class InputAudioConfig(proto.Message): single_utterance = proto.Field(proto.BOOL, number=8) + disable_no_speech_recognized_event = proto.Field(proto.BOOL, number=14) + class VoiceSelectionParams(proto.Message): r"""Description of which voice to use for speech synthesis. @@ -273,7 +308,7 @@ class VoiceSelectionParams(proto.Message): will choose a voice based on the other parameters such as language_code and [ssml_gender][google.cloud.dialogflow.v2.VoiceSelectionParams.ssml_gender]. - ssml_gender (~.audio_config.SsmlVoiceGender): + ssml_gender (google.cloud.dialogflow_v2.types.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 @@ -320,7 +355,7 @@ class SynthesizeSpeechConfig(proto.Message): synthesized) text to speech. Effects are applied on top of each other in the order they are given. - voice (~.audio_config.VoiceSelectionParams): + voice (google.cloud.dialogflow_v2.types.VoiceSelectionParams): Optional. The desired voice of the synthesized audio. """ @@ -343,7 +378,7 @@ class OutputAudioConfig(proto.Message): applied to the agent. Attributes: - audio_encoding (~.audio_config.OutputAudioEncoding): + audio_encoding (google.cloud.dialogflow_v2.types.OutputAudioEncoding): Required. Audio encoding of the synthesized audio content. sample_rate_hertz (int): @@ -355,7 +390,7 @@ class OutputAudioConfig(proto.Message): 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): + synthesize_speech_config (google.cloud.dialogflow_v2.types.SynthesizeSpeechConfig): Configuration of how speech should be synthesized. """ @@ -369,4 +404,33 @@ class OutputAudioConfig(proto.Message): ) +class TelephonyDtmfEvents(proto.Message): + r"""A wrapper of repeated TelephonyDtmf digits. + + Attributes: + dtmf_events (Sequence[google.cloud.dialogflow_v2.types.TelephonyDtmf]): + A sequence of TelephonyDtmf digits. + """ + + dtmf_events = proto.RepeatedField(proto.ENUM, number=1, enum="TelephonyDtmf",) + + +class SpeechToTextConfig(proto.Message): + r"""Configures speech transcription for + [ConversationProfile][google.cloud.dialogflow.v2.ConversationProfile]. + + Attributes: + speech_model_variant (google.cloud.dialogflow_v2.types.SpeechModelVariant): + Optional. The speech model used in speech to text. + ``SPEECH_MODEL_VARIANT_UNSPECIFIED``, ``USE_BEST_AVAILABLE`` + will be treated as ``USE_ENHANCED``. It can be overridden in + [AnalyzeContentRequest][google.cloud.dialogflow.v2.AnalyzeContentRequest] + and + [StreamingAnalyzeContentRequest][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest] + request. + """ + + speech_model_variant = proto.Field(proto.ENUM, number=1, enum="SpeechModelVariant",) + + __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2/types/context.py b/google/cloud/dialogflow_v2/types/context.py index 271978476..72e6564de 100644 --- a/google/cloud/dialogflow_v2/types/context.py +++ b/google/cloud/dialogflow_v2/types/context.py @@ -83,7 +83,7 @@ class Context(proto.Message): ``0``, the context expires immediately. Contexts expire automatically after 20 minutes if there are no matching queries. - parameters (~.struct.Struct): + parameters (google.protobuf.struct_pb2.Struct): Optional. The collection of parameters associated with this context. Depending on your protocol or client library @@ -95,9 +95,10 @@ class Context(proto.Message): - 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: + composite entity: map - Else: depending on + parameter value type, could be one of string, + number, boolean, null, list or map + - MapValue value: - If parameter's entity type is a composite entity: map from composite entity property names to property values - @@ -144,7 +145,7 @@ class ListContextsResponse(proto.Message): [Contexts.ListContexts][google.cloud.dialogflow.v2.Contexts.ListContexts]. Attributes: - contexts (Sequence[~.gcd_context.Context]): + contexts (Sequence[google.cloud.dialogflow_v2.types.Context]): The list of contexts. There will be a maximum number of items returned based on the page_size field in the request. next_page_token (str): @@ -192,7 +193,7 @@ class CreateContextRequest(proto.Message): If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. - context (~.gcd_context.Context): + context (google.cloud.dialogflow_v2.types.Context): Required. The context to create. """ @@ -206,9 +207,9 @@ class UpdateContextRequest(proto.Message): [Contexts.UpdateContext][google.cloud.dialogflow.v2.Contexts.UpdateContext]. Attributes: - context (~.gcd_context.Context): + context (google.cloud.dialogflow_v2.types.Context): Required. The context to update. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ diff --git a/google/cloud/dialogflow_v2/types/conversation.py b/google/cloud/dialogflow_v2/types/conversation.py new file mode 100644 index 000000000..7548e1730 --- /dev/null +++ b/google/cloud/dialogflow_v2/types/conversation.py @@ -0,0 +1,497 @@ +# -*- 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.dialogflow_v2.types import participant +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.v2", + manifest={ + "Conversation", + "CallMatcher", + "CreateConversationRequest", + "ListConversationsRequest", + "ListConversationsResponse", + "GetConversationRequest", + "CompleteConversationRequest", + "CreateCallMatcherRequest", + "ListCallMatchersRequest", + "ListCallMatchersResponse", + "DeleteCallMatcherRequest", + "ListMessagesRequest", + "ListMessagesResponse", + "ConversationPhoneNumber", + }, +) + + +class Conversation(proto.Message): + r"""Represents a conversation. + A conversation is an interaction between an agent, including + live agents and Dialogflow agents, and a support customer. + Conversations can include phone calls and text-based chat + sessions. + + Attributes: + name (str): + Output only. The unique identifier of this conversation. + Format: + ``projects//locations//conversations/``. + lifecycle_state (google.cloud.dialogflow_v2.types.Conversation.LifecycleState): + Output only. The current state of the + Conversation. + conversation_profile (str): + Required. The Conversation Profile to be used to configure + this Conversation. This field cannot be updated. Format: + ``projects//locations//conversationProfiles/``. + phone_number (google.cloud.dialogflow_v2.types.ConversationPhoneNumber): + Output only. It will not be empty if the + conversation is to be connected over telephony. + start_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The time the conversation was + started. + end_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The time the conversation was + finished. + conversation_stage (google.cloud.dialogflow_v2.types.Conversation.ConversationStage): + The stage of a conversation. It indicates whether the + virtual agent or a human agent is handling the conversation. + + If the conversation is created with the conversation profile + that has Dialogflow config set, defaults to + [ConversationStage.VIRTUAL_AGENT_STAGE][google.cloud.dialogflow.v2.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE]; + Otherwise, defaults to + [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2.Conversation.ConversationStage.HUMAN_ASSIST_STAGE]. + + If the conversation is created with the conversation profile + that has Dialogflow config set but explicitly sets + conversation_stage to + [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2.Conversation.ConversationStage.HUMAN_ASSIST_STAGE], + it skips + [ConversationStage.VIRTUAL_AGENT_STAGE][google.cloud.dialogflow.v2.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE] + stage and directly goes to + [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2.Conversation.ConversationStage.HUMAN_ASSIST_STAGE]. + """ + + class LifecycleState(proto.Enum): + r"""Enumeration of the completion status of the conversation.""" + LIFECYCLE_STATE_UNSPECIFIED = 0 + IN_PROGRESS = 1 + COMPLETED = 2 + + class ConversationStage(proto.Enum): + r"""Enumeration of the different conversation stages a + conversation can be in. Reference: + https://cloud.google.com/dialogflow/priv/docs/contact- + center/basics#stages + """ + CONVERSATION_STAGE_UNSPECIFIED = 0 + VIRTUAL_AGENT_STAGE = 1 + HUMAN_ASSIST_STAGE = 2 + + name = proto.Field(proto.STRING, number=1) + + lifecycle_state = proto.Field(proto.ENUM, number=2, enum=LifecycleState,) + + conversation_profile = proto.Field(proto.STRING, number=3) + + phone_number = proto.Field( + proto.MESSAGE, number=4, message="ConversationPhoneNumber", + ) + + start_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) + + end_time = proto.Field(proto.MESSAGE, number=6, message=timestamp.Timestamp,) + + conversation_stage = proto.Field(proto.ENUM, number=7, enum=ConversationStage,) + + +class CallMatcher(proto.Message): + r"""Represents a call matcher that describes criteria for matching + incoming SIP calls to a conversation. When Dialogflow get a SIP call + from a third-party carrier, Dialogflow matches the call to an + existing conversation by either: + + - Extracting the conversation id from the `Call-Info + header `__, + e.g. + ``Call-Info: ;purpose=Goog-ContactCenter-Conversation``. + - Or, if that doesn't work, matching incoming `SIP + headers `__ + against any [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] + for the conversation. + + If an incoming SIP call without valid ``Call-Info`` header matches + to zero or multiple conversations with ``CallMatcher``, we reject + it. + + A call matcher contains equality conditions for SIP headers that all + have to be fulfilled in order for a SIP call to match. + + The matched SIP headers consist of well-known headers (``To``, + ``From``, ``Call-ID``) and custom headers. A + [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] is only valid + if it specifies: + + - At least 1 custom header, + - or at least 2 well-known headers. + + Attributes: + name (str): + Output only. The unique identifier of this call matcher. + Format: + ``projects//locations//conversations//callMatchers/``. + to_header (str): + Value of the ```To`` + header `__ + to match. If empty or unspecified, we don't match to the + ```To`` + header `__. + from_header (str): + Value of the ```From`` + header `__ + to match. If empty or unspecified, we don't match to the + ```From`` + header `__. + call_id_header (str): + Value of the ```Call-ID`` + header `__ + to match. If empty or unspecified, we don't match to the + ```Call-ID`` + header `__. + custom_headers (google.cloud.dialogflow_v2.types.CallMatcher.CustomHeaders): + Custom SIP headers that must match. + """ + + class CustomHeaders(proto.Message): + r"""Custom SIP headers. See the `description of headers in the + RFC `__. + + Attributes: + cisco_guid (str): + Cisco's proprietary ``Cisco-Guid`` header. + """ + + cisco_guid = proto.Field(proto.STRING, number=1) + + name = proto.Field(proto.STRING, number=1) + + to_header = proto.Field(proto.STRING, number=2) + + from_header = proto.Field(proto.STRING, number=3) + + call_id_header = proto.Field(proto.STRING, number=4) + + custom_headers = proto.Field(proto.MESSAGE, number=5, message=CustomHeaders,) + + +class CreateConversationRequest(proto.Message): + r"""The request message for + [Conversations.CreateConversation][google.cloud.dialogflow.v2.Conversations.CreateConversation]. + + Attributes: + parent (str): + Required. Resource identifier of the project creating the + conversation. Format: + ``projects//locations/``. + conversation (google.cloud.dialogflow_v2.types.Conversation): + Required. The conversation to create. + conversation_id (str): + Optional. Identifier of the conversation. Generally it's + auto generated by Google. Only set it if you cannot wait for + the response to return a auto-generated one to you. + + The conversation ID must be compliant with the regression + fomula "[a-zA-Z][a-zA-Z0-9_-]*" with the characters length + in range of [3,64]. If the field is provided, the caller is + resposible for + + 1. the uniqueness of the ID, otherwise the request will be + rejected. + 2. the consistency for whether to use custom ID or not under + a project to better ensure uniqueness. + """ + + parent = proto.Field(proto.STRING, number=1) + + conversation = proto.Field(proto.MESSAGE, number=2, message="Conversation",) + + conversation_id = proto.Field(proto.STRING, number=3) + + +class ListConversationsRequest(proto.Message): + r"""The request message for + [Conversations.ListConversations][google.cloud.dialogflow.v2.Conversations.ListConversations]. + + Attributes: + parent (str): + Required. The project from which to list all conversation. + Format: ``projects//locations/``. + page_size (int): + Optional. The maximum number of items to + return in a single page. By default 100 and at + most 1000. + page_token (str): + Optional. The next_page_token value returned from a previous + list request. + filter (str): + A filter expression that filters conversations listed in the + response. In general, the expression must specify the field + name, a comparison operator, and the value to use for + filtering: + + .. raw:: html + +
    +
  • The value must be a string, a number, or a boolean.
  • +
  • The comparison operator must be either `=`,`!=`, `>`, or `<`.
  • +
  • To filter on multiple expressions, separate the + expressions with `AND` or `OR` (omitting both implies `AND`).
  • +
  • For clarity, expressions can be enclosed in parentheses.
  • +
+ Only `lifecycle_state` can be filtered on in this way. For example, + the following expression only returns `COMPLETED` conversations: + + ``lifecycle_state = "COMPLETED"`` + + For more information about filtering, see `API + Filtering `__. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + filter = proto.Field(proto.STRING, number=4) + + +class ListConversationsResponse(proto.Message): + r"""The response message for + [Conversations.ListConversations][google.cloud.dialogflow.v2.Conversations.ListConversations]. + + Attributes: + conversations (Sequence[google.cloud.dialogflow_v2.types.Conversation]): + The list of conversations. 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 + + conversations = proto.RepeatedField( + proto.MESSAGE, number=1, message="Conversation", + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetConversationRequest(proto.Message): + r"""The request message for + [Conversations.GetConversation][google.cloud.dialogflow.v2.Conversations.GetConversation]. + + Attributes: + name (str): + Required. The name of the conversation. Format: + ``projects//locations//conversations/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CompleteConversationRequest(proto.Message): + r"""The request message for + [Conversations.CompleteConversation][google.cloud.dialogflow.v2.Conversations.CompleteConversation]. + + Attributes: + name (str): + Required. Resource identifier of the conversation to close. + Format: + ``projects//locations//conversations/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreateCallMatcherRequest(proto.Message): + r"""The request message for + [Conversations.CreateCallMatcher][google.cloud.dialogflow.v2.Conversations.CreateCallMatcher]. + + Attributes: + parent (str): + Required. Resource identifier of the conversation adding the + call matcher. Format: + ``projects//locations//conversations/``. + call_matcher (google.cloud.dialogflow_v2.types.CallMatcher): + Required. The call matcher to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + call_matcher = proto.Field(proto.MESSAGE, number=2, message="CallMatcher",) + + +class ListCallMatchersRequest(proto.Message): + r"""The request message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2.Conversations.ListCallMatchers]. + + Attributes: + parent (str): + Required. The conversation to list all call matchers from. + Format: + ``projects//locations//conversations/``. + page_size (int): + Optional. The maximum number of items to + return in a single page. By default 100 and at + most 1000. + page_token (str): + Optional. 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 ListCallMatchersResponse(proto.Message): + r"""The response message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2.Conversations.ListCallMatchers]. + + Attributes: + call_matchers (Sequence[google.cloud.dialogflow_v2.types.CallMatcher]): + The list of call matchers. There is 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 + + call_matchers = proto.RepeatedField(proto.MESSAGE, number=1, message="CallMatcher",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class DeleteCallMatcherRequest(proto.Message): + r"""The request message for + [Conversations.DeleteCallMatcher][google.cloud.dialogflow.v2.Conversations.DeleteCallMatcher]. + + Attributes: + name (str): + Required. The unique identifier of the + [CallMatcher][google.cloud.dialogflow.v2.CallMatcher] to + delete. Format: + ``projects//locations//conversations//callMatchers/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class ListMessagesRequest(proto.Message): + r"""The request message for + [Conversations.ListMessages][google.cloud.dialogflow.v2.Conversations.ListMessages]. + + Attributes: + parent (str): + Required. The name of the conversation to list messages for. + Format: + ``projects//locations//conversations/`` + filter (str): + Optional. Filter on message fields. Currently predicates on + ``create_time`` and ``create_time_epoch_microseconds`` are + supported. ``create_time`` only support milliseconds + accuracy. E.g., + ``create_time_epoch_microseconds > 1551790877964485`` or + ``create_time > 2017-01-15T01:30:15.01Z``. + + For more information about filtering, see `API + Filtering `__. + page_size (int): + Optional. The maximum number of items to + return in a single page. By default 100 and at + most 1000. + page_token (str): + Optional. The next_page_token value returned from a previous + list request. + """ + + parent = proto.Field(proto.STRING, number=1) + + filter = proto.Field(proto.STRING, number=4) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class ListMessagesResponse(proto.Message): + r"""The response message for + [Conversations.ListMessages][google.cloud.dialogflow.v2.Conversations.ListMessages]. + + Attributes: + messages (Sequence[google.cloud.dialogflow_v2.types.Message]): + The list of messages. There will be a maximum number of + items returned based on the page_size field in the request. + ``messages`` is sorted by ``create_time`` in descending + order. + 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 + + messages = proto.RepeatedField( + proto.MESSAGE, number=1, message=participant.Message, + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class ConversationPhoneNumber(proto.Message): + r"""Represents a phone number for telephony integration. It + allows for connecting a particular conversation over telephony. + + Attributes: + phone_number (str): + Output only. The phone number to connect to + this conversation. + """ + + phone_number = proto.Field(proto.STRING, number=3) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2/types/conversation_event.py b/google/cloud/dialogflow_v2/types/conversation_event.py new file mode 100644 index 000000000..1f6551e7a --- /dev/null +++ b/google/cloud/dialogflow_v2/types/conversation_event.py @@ -0,0 +1,69 @@ +# -*- 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.dialogflow_v2.types import participant +from google.rpc import status_pb2 as status # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.v2", manifest={"ConversationEvent",}, +) + + +class ConversationEvent(proto.Message): + r"""Represents a notification sent to Pub/Sub subscribers for + conversation lifecycle events. + + Attributes: + conversation (str): + The unique identifier of the conversation this notification + refers to. Format: + ``projects//conversations/``. + type_ (google.cloud.dialogflow_v2.types.ConversationEvent.Type): + The type of the event that this notification + refers to. + error_status (google.rpc.status_pb2.Status): + More detailed information about an error. Only set for type + UNRECOVERABLE_ERROR_IN_PHONE_CALL. + new_message_payload (google.cloud.dialogflow_v2.types.Message): + Payload of NEW_MESSAGE event. + """ + + class Type(proto.Enum): + r"""Enumeration of the types of events available.""" + TYPE_UNSPECIFIED = 0 + CONVERSATION_STARTED = 1 + CONVERSATION_FINISHED = 2 + HUMAN_INTERVENTION_NEEDED = 3 + NEW_MESSAGE = 5 + UNRECOVERABLE_ERROR = 4 + + conversation = proto.Field(proto.STRING, number=1) + + type_ = proto.Field(proto.ENUM, number=2, enum=Type,) + + error_status = proto.Field(proto.MESSAGE, number=3, message=status.Status,) + + new_message_payload = proto.Field( + proto.MESSAGE, number=4, oneof="payload", message=participant.Message, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2/types/conversation_profile.py b/google/cloud/dialogflow_v2/types/conversation_profile.py new file mode 100644 index 000000000..c792728dd --- /dev/null +++ b/google/cloud/dialogflow_v2/types/conversation_profile.py @@ -0,0 +1,724 @@ +# -*- 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.dialogflow_v2.types import audio_config +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.v2", + manifest={ + "ConversationProfile", + "ListConversationProfilesRequest", + "ListConversationProfilesResponse", + "GetConversationProfileRequest", + "CreateConversationProfileRequest", + "UpdateConversationProfileRequest", + "DeleteConversationProfileRequest", + "AutomatedAgentConfig", + "HumanAgentAssistantConfig", + "HumanAgentHandoffConfig", + "NotificationConfig", + "LoggingConfig", + "SuggestionFeature", + }, +) + + +class ConversationProfile(proto.Message): + r"""Defines the services to connect to incoming Dialogflow + conversations. + + Attributes: + name (str): + Optional. The unique identifier of this conversation + profile. Format: + ``projects//locations//conversationProfiles/``. + display_name (str): + Required. Human readable name for this + profile. Max length 1024 bytes. + create_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Create time of the conversation + profile. + update_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Update time of the conversation + profile. + automated_agent_config (google.cloud.dialogflow_v2.types.AutomatedAgentConfig): + Configuration for an automated agent to use + with this profile. + human_agent_assistant_config (google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig): + Configuration for agent assistance to use + with this profile. + human_agent_handoff_config (google.cloud.dialogflow_v2.types.HumanAgentHandoffConfig): + Configuration for connecting to a live agent. + notification_config (google.cloud.dialogflow_v2.types.NotificationConfig): + Configuration for publishing conversation + lifecycle events. + logging_config (google.cloud.dialogflow_v2.types.LoggingConfig): + Configuration for logging conversation + lifecycle events. + new_message_event_notification_config (google.cloud.dialogflow_v2.types.NotificationConfig): + Configuration for publishing new message events. Event will + be sent in format of + [ConversationEvent][google.cloud.dialogflow.v2.ConversationEvent] + stt_config (google.cloud.dialogflow_v2.types.SpeechToTextConfig): + Settings for speech transcription. + language_code (str): + Language which represents the + conversationProfile. If unspecified, the default + language code en-us applies. Users need to + create a ConversationProfile for each language + they want to support. + """ + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + create_time = proto.Field(proto.MESSAGE, number=11, message=timestamp.Timestamp,) + + update_time = proto.Field(proto.MESSAGE, number=12, message=timestamp.Timestamp,) + + automated_agent_config = proto.Field( + proto.MESSAGE, number=3, message="AutomatedAgentConfig", + ) + + human_agent_assistant_config = proto.Field( + proto.MESSAGE, number=4, message="HumanAgentAssistantConfig", + ) + + human_agent_handoff_config = proto.Field( + proto.MESSAGE, number=5, message="HumanAgentHandoffConfig", + ) + + notification_config = proto.Field( + proto.MESSAGE, number=6, message="NotificationConfig", + ) + + logging_config = proto.Field(proto.MESSAGE, number=7, message="LoggingConfig",) + + new_message_event_notification_config = proto.Field( + proto.MESSAGE, number=8, message="NotificationConfig", + ) + + stt_config = proto.Field( + proto.MESSAGE, number=9, message=audio_config.SpeechToTextConfig, + ) + + language_code = proto.Field(proto.STRING, number=10) + + +class ListConversationProfilesRequest(proto.Message): + r"""The request message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2.ConversationProfiles.ListConversationProfiles]. + + Attributes: + parent (str): + Required. The project to list all conversation profiles + from. 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 ListConversationProfilesResponse(proto.Message): + r"""The response message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2.ConversationProfiles.ListConversationProfiles]. + + Attributes: + conversation_profiles (Sequence[google.cloud.dialogflow_v2.types.ConversationProfile]): + The list of project conversation profiles. There is 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 + + conversation_profiles = proto.RepeatedField( + proto.MESSAGE, number=1, message="ConversationProfile", + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetConversationProfileRequest(proto.Message): + r"""The request message for + [ConversationProfiles.GetConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.GetConversationProfile]. + + Attributes: + name (str): + Required. The resource name of the conversation profile. + Format: + ``projects//locations//conversationProfiles/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreateConversationProfileRequest(proto.Message): + r"""The request message for + [ConversationProfiles.CreateConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.CreateConversationProfile]. + + Attributes: + parent (str): + Required. The project to create a conversation profile for. + Format: ``projects//locations/``. + conversation_profile (google.cloud.dialogflow_v2.types.ConversationProfile): + Required. The conversation profile to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + conversation_profile = proto.Field( + proto.MESSAGE, number=2, message="ConversationProfile", + ) + + +class UpdateConversationProfileRequest(proto.Message): + r"""The request message for + [ConversationProfiles.UpdateConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.UpdateConversationProfile]. + + Attributes: + conversation_profile (google.cloud.dialogflow_v2.types.ConversationProfile): + Required. The conversation profile to update. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. The mask to control which fields to + update. + """ + + conversation_profile = proto.Field( + proto.MESSAGE, number=1, message="ConversationProfile", + ) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class DeleteConversationProfileRequest(proto.Message): + r"""The request message for + [ConversationProfiles.DeleteConversationProfile][google.cloud.dialogflow.v2.ConversationProfiles.DeleteConversationProfile]. + + This operation fails if the conversation profile is still referenced + from a phone number. + + Attributes: + name (str): + Required. The name of the conversation profile to delete. + Format: + ``projects//locations//conversationProfiles/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class AutomatedAgentConfig(proto.Message): + r"""Defines the Automated Agent to connect to a conversation. + + Attributes: + agent (str): + Required. ID of the Dialogflow agent environment to use. + + This project needs to either be the same project as the + conversation or you need to grant + ``service-@gcp-sa-dialogflow.iam.gserviceaccount.com`` + the ``Dialogflow API Service Agent`` role in this project. + + Format: + ``projects//locations//agent/environments/``. + If environment is not specified, the default ``draft`` + environment is used. Refer to + `DetectIntentRequest `__ + for more details. + """ + + agent = proto.Field(proto.STRING, number=1) + + +class HumanAgentAssistantConfig(proto.Message): + r"""Defines the Human Agent Assist to connect to a conversation. + + Attributes: + notification_config (google.cloud.dialogflow_v2.types.NotificationConfig): + Pub/Sub topic on which to publish new agent + assistant events. + human_agent_suggestion_config (google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig.SuggestionConfig): + Configuration for agent assistance of human + agent participant. + end_user_suggestion_config (google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig.SuggestionConfig): + Configuration for agent assistance of end + user participant. + message_analysis_config (google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig.MessageAnalysisConfig): + Configuration for message analysis. + """ + + class SuggestionTriggerSettings(proto.Message): + r"""Settings of suggestion trigger. + + Attributes: + no_smalltalk (bool): + Do not trigger if last utterance is small + talk. + only_end_user (bool): + Only trigger suggestion if participant role of last + utterance is END_USER. + """ + + no_smalltalk = proto.Field(proto.BOOL, number=1) + + only_end_user = proto.Field(proto.BOOL, number=2) + + class SuggestionFeatureConfig(proto.Message): + r"""Config for suggestion features. + + Attributes: + suggestion_feature (google.cloud.dialogflow_v2.types.SuggestionFeature): + The suggestion feature. + enable_event_based_suggestion (bool): + Automatically iterates all participants and tries to compile + suggestions. + + Supported features: ARTICLE_SUGGESTION, FAQ, + DIALOGFLOW_ASSIST. + suggestion_trigger_settings (google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig.SuggestionTriggerSettings): + Settings of suggestion trigger. + + Currently, only ARTICLE_SUGGESTION and FAQ will use this + field. + query_config (google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig.SuggestionQueryConfig): + Configs of query. + conversation_model_config (google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig.ConversationModelConfig): + Configs of custom conversation model. + """ + + suggestion_feature = proto.Field( + proto.MESSAGE, number=5, message="SuggestionFeature", + ) + + enable_event_based_suggestion = proto.Field(proto.BOOL, number=3) + + suggestion_trigger_settings = proto.Field( + proto.MESSAGE, + number=10, + message="HumanAgentAssistantConfig.SuggestionTriggerSettings", + ) + + query_config = proto.Field( + proto.MESSAGE, + number=6, + message="HumanAgentAssistantConfig.SuggestionQueryConfig", + ) + + conversation_model_config = proto.Field( + proto.MESSAGE, + number=7, + message="HumanAgentAssistantConfig.ConversationModelConfig", + ) + + class SuggestionConfig(proto.Message): + r"""Detail human agent assistant config. + + Attributes: + feature_configs (Sequence[google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig.SuggestionFeatureConfig]): + Configuration of different suggestion + features. One feature can have only one config. + group_suggestion_responses (bool): + If ``group_suggestion_responses`` is false, and there are + multiple ``feature_configs`` in ``event based suggestion`` + or StreamingAnalyzeContent, we will try to deliver + suggestions to customers as soon as we get new suggestion. + Different type of suggestions based on the same context will + be in separate Pub/Sub event or + ``StreamingAnalyzeContentResponse``. + + If ``group_suggestion_responses`` set to true. All the + suggestions to the same participant based on the same + context will be grouped into a single Pub/Sub event or + StreamingAnalyzeContentResponse. + """ + + feature_configs = proto.RepeatedField( + proto.MESSAGE, + number=2, + message="HumanAgentAssistantConfig.SuggestionFeatureConfig", + ) + + group_suggestion_responses = proto.Field(proto.BOOL, number=3) + + class SuggestionQueryConfig(proto.Message): + r"""Config for suggestion query. + + Attributes: + knowledge_base_query_source (google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig.SuggestionQueryConfig.KnowledgeBaseQuerySource): + Query from knowledgebase. It is used by: ARTICLE_SUGGESTION, + FAQ. + document_query_source (google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig.SuggestionQueryConfig.DocumentQuerySource): + Query from knowledge base document. It is used by: + SMART_REPLY, SMART_COMPOSE. + dialogflow_query_source (google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig.SuggestionQueryConfig.DialogflowQuerySource): + Query from Dialogflow agent. It is used by + DIALOGFLOW_ASSIST. + max_results (int): + Maximum number of results to return. + Currently, if unset, defaults to 10. And the max + number is 20. + confidence_threshold (float): + Confidence threshold of query result. + + Agent Assist gives each suggestion a score in the range + [0.0, 1.0], based on the relevance between the suggestion + and the current conversation context. A score of 0.0 has no + relevance, while a score of 1.0 has high relevance. Only + suggestions with a score greater than or equal to the value + of this field are included in the results. + + For a baseline model (the default), the recommended value is + in the range [0.05, 0.1]. + + For a custom model, there is no recommended value. Tune this + value by starting from a very low value and slowly + increasing until you have desired results. + + If this field is not set, it defaults to 0.0, which means + that all suggestions are returned. + + Supported features: ARTICLE_SUGGESTION. + context_filter_settings (google.cloud.dialogflow_v2.types.HumanAgentAssistantConfig.SuggestionQueryConfig.ContextFilterSettings): + Determines how recent conversation context is + filtered when generating suggestions. If + unspecified, no messages will be dropped. + """ + + class KnowledgeBaseQuerySource(proto.Message): + r"""Knowledge base source settings. + + Supported features: ARTICLE_SUGGESTION, FAQ. + + Attributes: + knowledge_bases (Sequence[str]): + Required. Knowledge bases to query. Format: + ``projects//locations//knowledgeBases/``. + Currently, at most 5 knowledge bases are supported. + """ + + knowledge_bases = proto.RepeatedField(proto.STRING, number=1) + + class DocumentQuerySource(proto.Message): + r"""Document source settings. + + Supported features: SMART_REPLY, SMART_COMPOSE. + + Attributes: + documents (Sequence[str]): + Required. Knowledge documents to query from. Format: + ``projects//locations//knowledgeBases//documents/``. + Currently, at most 5 documents are supported. + """ + + documents = proto.RepeatedField(proto.STRING, number=1) + + class DialogflowQuerySource(proto.Message): + r"""Dialogflow source setting. + + Supported feature: DIALOGFLOW_ASSIST. + + Attributes: + agent (str): + Required. The name of a Dialogflow virtual agent used for + end user side intent detection and suggestion. Format: + ``projects//locations//agent``. + When multiple agents are allowed in the same Dialogflow + project. + """ + + agent = proto.Field(proto.STRING, number=1) + + class ContextFilterSettings(proto.Message): + r"""Settings that determine how to filter recent conversation + context when generating suggestions. + + Attributes: + drop_handoff_messages (bool): + If set to true, the last message from virtual + agent (hand off message) and the message before + it (trigger message of hand off) are dropped. + drop_virtual_agent_messages (bool): + If set to true, all messages from virtual + agent are dropped. + drop_ivr_messages (bool): + If set to true, all messages from ivr stage + are dropped. + """ + + drop_handoff_messages = proto.Field(proto.BOOL, number=1) + + drop_virtual_agent_messages = proto.Field(proto.BOOL, number=2) + + drop_ivr_messages = proto.Field(proto.BOOL, number=3) + + knowledge_base_query_source = proto.Field( + proto.MESSAGE, + number=1, + oneof="query_source", + message="HumanAgentAssistantConfig.SuggestionQueryConfig.KnowledgeBaseQuerySource", + ) + + document_query_source = proto.Field( + proto.MESSAGE, + number=2, + oneof="query_source", + message="HumanAgentAssistantConfig.SuggestionQueryConfig.DocumentQuerySource", + ) + + dialogflow_query_source = proto.Field( + proto.MESSAGE, + number=3, + oneof="query_source", + message="HumanAgentAssistantConfig.SuggestionQueryConfig.DialogflowQuerySource", + ) + + max_results = proto.Field(proto.INT32, number=4) + + confidence_threshold = proto.Field(proto.FLOAT, number=5) + + context_filter_settings = proto.Field( + proto.MESSAGE, + number=7, + message="HumanAgentAssistantConfig.SuggestionQueryConfig.ContextFilterSettings", + ) + + class ConversationModelConfig(proto.Message): + r"""Custom conversation models used in agent assist feature. + + Supported feature: ARTICLE_SUGGESTION, SMART_COMPOSE, SMART_REPLY. + + Attributes: + model (str): + Required. Conversation model resource name. Format: + ``projects//conversationModels/``. + """ + + model = proto.Field(proto.STRING, number=1) + + class MessageAnalysisConfig(proto.Message): + r"""Configuration for analyses to run on each conversation + message. + + Attributes: + enable_entity_extraction (bool): + Enable entity extraction in conversation messages on `agent + assist + stage `__. + If unspecified, defaults to false. + enable_sentiment_analysis (bool): + Enable sentiment analysis in conversation messages on `agent + assist + stage `__. + If unspecified, defaults to false. Sentiment analysis + inspects user input and identifies the prevailing subjective + opinion, especially to determine a user's attitude as + positive, negative, or neutral: + https://cloud.google.com/natural-language/docs/basics#sentiment_analysis + For + [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2.Participants.StreamingAnalyzeContent] + method, result will be in + [StreamingAnalyzeContentResponse.message.SentimentAnalysisResult][google.cloud.dialogflow.v2.StreamingAnalyzeContentResponse.message]. + For + [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent] + method, result will be in + [AnalyzeContentResponse.message.SentimentAnalysisResult][google.cloud.dialogflow.v2.AnalyzeContentResponse.message] + For + [Conversations.ListMessages][google.cloud.dialogflow.v2.Conversations.ListMessages] + method, result will be in + [ListMessagesResponse.messages.SentimentAnalysisResult][google.cloud.dialogflow.v2.ListMessagesResponse.messages] + If Pub/Sub notification is configured, result will be in + [ConversationEvent.new_message_payload.SentimentAnalysisResult][google.cloud.dialogflow.v2.ConversationEvent.new_message_payload]. + """ + + enable_entity_extraction = proto.Field(proto.BOOL, number=2) + + enable_sentiment_analysis = proto.Field(proto.BOOL, number=3) + + notification_config = proto.Field( + proto.MESSAGE, number=2, message="NotificationConfig", + ) + + human_agent_suggestion_config = proto.Field( + proto.MESSAGE, number=3, message=SuggestionConfig, + ) + + end_user_suggestion_config = proto.Field( + proto.MESSAGE, number=4, message=SuggestionConfig, + ) + + message_analysis_config = proto.Field( + proto.MESSAGE, number=5, message=MessageAnalysisConfig, + ) + + +class HumanAgentHandoffConfig(proto.Message): + r"""Defines the hand off to a live agent, typically on which + external agent service provider to connect to a conversation. + + Attributes: + live_person_config (google.cloud.dialogflow_v2.types.HumanAgentHandoffConfig.LivePersonConfig): + Uses LivePerson (https://www.liveperson.com). + salesforce_live_agent_config (google.cloud.dialogflow_v2.types.HumanAgentHandoffConfig.SalesforceLiveAgentConfig): + Uses Salesforce Live Agent. + """ + + class LivePersonConfig(proto.Message): + r"""Configuration specific to LivePerson + (https://www.liveperson.com). + + Attributes: + account_number (str): + Required. Account number of the LivePerson + account to connect. This is the account number + you input at the login page. + """ + + account_number = proto.Field(proto.STRING, number=1) + + class SalesforceLiveAgentConfig(proto.Message): + r"""Configuration specific to Salesforce Live Agent. + + Attributes: + organization_id (str): + Required. The organization ID of the + Salesforce account. + deployment_id (str): + Required. Live Agent deployment ID. + button_id (str): + Required. Live Agent chat button ID. + endpoint_domain (str): + Required. Domain of the Live Agent endpoint for this agent. + You can find the endpoint URL in the ``Live Agent settings`` + page. For example if URL has the form + https://d.la4-c2-phx.salesforceliveagent.com/..., you should + fill in d.la4-c2-phx.salesforceliveagent.com. + """ + + organization_id = proto.Field(proto.STRING, number=1) + + deployment_id = proto.Field(proto.STRING, number=2) + + button_id = proto.Field(proto.STRING, number=3) + + endpoint_domain = proto.Field(proto.STRING, number=4) + + live_person_config = proto.Field( + proto.MESSAGE, number=1, oneof="agent_service", message=LivePersonConfig, + ) + + salesforce_live_agent_config = proto.Field( + proto.MESSAGE, + number=2, + oneof="agent_service", + message=SalesforceLiveAgentConfig, + ) + + +class NotificationConfig(proto.Message): + r"""Defines notification behavior. + + Attributes: + topic (str): + Name of the Pub/Sub topic to publish conversation events + like + [CONVERSATION_STARTED][google.cloud.dialogflow.v2.ConversationEvent.Type.CONVERSATION_STARTED] + as serialized + [ConversationEvent][google.cloud.dialogflow.v2.ConversationEvent] + protos. + + Notification works for phone calls, if this topic either is + in the same project as the conversation or you grant + ``service-@gcp-sa-dialogflow.iam.gserviceaccount.com`` + the ``Dialogflow Service Agent`` role in the topic project. + + Format: + ``projects//locations//topics/``. + message_format (google.cloud.dialogflow_v2.types.NotificationConfig.MessageFormat): + Format of message. + """ + + class MessageFormat(proto.Enum): + r"""Format of cloud pub/sub message.""" + MESSAGE_FORMAT_UNSPECIFIED = 0 + PROTO = 1 + JSON = 2 + + topic = proto.Field(proto.STRING, number=1) + + message_format = proto.Field(proto.ENUM, number=2, enum=MessageFormat,) + + +class LoggingConfig(proto.Message): + r"""Defines logging behavior for conversation lifecycle events. + + Attributes: + enable_stackdriver_logging (bool): + Whether to log conversation events like + [CONVERSATION_STARTED][google.cloud.dialogflow.v2.ConversationEvent.Type.CONVERSATION_STARTED] + to Stackdriver in the conversation project as JSON format + [ConversationEvent][google.cloud.dialogflow.v2.ConversationEvent] + protos. + """ + + enable_stackdriver_logging = proto.Field(proto.BOOL, number=3) + + +class SuggestionFeature(proto.Message): + r"""The type of Human Agent Assistant API suggestion to perform, and the + maximum number of results to return for that type. Multiple + ``Feature`` objects can be specified in the ``features`` list. + + Attributes: + type_ (google.cloud.dialogflow_v2.types.SuggestionFeature.Type): + Type of Human Agent Assistant API feature to + request. + """ + + class Type(proto.Enum): + r"""Defines the type of Human Agent Assistant feature.""" + TYPE_UNSPECIFIED = 0 + ARTICLE_SUGGESTION = 1 + FAQ = 2 + + type_ = proto.Field(proto.ENUM, number=1, enum=Type,) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2/types/document.py b/google/cloud/dialogflow_v2/types/document.py new file mode 100644 index 000000000..d04fe2188 --- /dev/null +++ b/google/cloud/dialogflow_v2/types/document.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 field_mask_pb2 as field_mask # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore +from google.rpc import status_pb2 as gr_status # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.v2", + manifest={ + "Document", + "GetDocumentRequest", + "ListDocumentsRequest", + "ListDocumentsResponse", + "CreateDocumentRequest", + "DeleteDocumentRequest", + "UpdateDocumentRequest", + "ReloadDocumentRequest", + "KnowledgeOperationMetadata", + }, +) + + +class Document(proto.Message): + r"""A knowledge document to be used by a + [KnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBase]. + + For more information, see the `knowledge base + guide `__. + + Note: The ``projects.agent.knowledgeBases.documents`` resource is + deprecated; only use ``projects.knowledgeBases.documents``. + + Attributes: + name (str): + Optional. The document resource name. The name must be empty + when creating a document. Format: + ``projects//locations//knowledgeBases//documents/``. + display_name (str): + Required. The display name of the document. + The name must be 1024 bytes or less; otherwise, + the creation request fails. + mime_type (str): + Required. The MIME type of this document. + knowledge_types (Sequence[google.cloud.dialogflow_v2.types.Document.KnowledgeType]): + Required. The knowledge type of document + content. + content_uri (str): + The URI where the file content is located. + + For documents stored in Google Cloud Storage, these URIs + must have the form ``gs:///``. + + NOTE: External URLs must correspond to public webpages, + i.e., they must be indexed by Google Search. In particular, + URLs for showing documents in Google Cloud Storage (i.e. the + URL in your browser) are not supported. Instead use the + ``gs://`` format URI described above. + raw_content (bytes): + The raw content of the document. This field is only + permitted for EXTRACTIVE_QA and FAQ knowledge types. + enable_auto_reload (bool): + Optional. If true, we try to automatically reload the + document every day (at a time picked by the system). If + false or unspecified, we don't try to automatically reload + the document. + + Currently you can only enable automatic reload for documents + sourced from a public url, see ``source`` field for the + source types. + + Reload status can be tracked in ``latest_reload_status``. If + a reload fails, we will keep the document unchanged. + + If a reload fails with internal errors, the system will try + to reload the document on the next day. If a reload fails + with non-retriable errors (e.g. PERMISION_DENIED), the + system will not try to reload the document anymore. You need + to manually reload the document successfully by calling + ``ReloadDocument`` and clear the errors. + latest_reload_status (google.cloud.dialogflow_v2.types.Document.ReloadStatus): + Output only. The time and status of the + latest reload. This reload may have been + triggered automatically or manually and may not + have succeeded. + metadata (Sequence[google.cloud.dialogflow_v2.types.Document.MetadataEntry]): + Optional. Metadata for the document. The metadata supports + arbitrary key-value pairs. Suggested use cases include + storing a document's title, an external URL distinct from + the document's content_uri, etc. The max size of a ``key`` + or a ``value`` of the metadata is 1024 bytes. + """ + + class KnowledgeType(proto.Enum): + r"""The knowledge type of document content.""" + KNOWLEDGE_TYPE_UNSPECIFIED = 0 + FAQ = 1 + EXTRACTIVE_QA = 2 + ARTICLE_SUGGESTION = 3 + + class ReloadStatus(proto.Message): + r"""The status of a reload attempt. + + Attributes: + time (google.protobuf.timestamp_pb2.Timestamp): + The time of a reload attempt. + This reload may have been triggered + automatically or manually and may not have + succeeded. + status (google.rpc.status_pb2.Status): + The status of a reload attempt or the initial + load. + """ + + time = proto.Field(proto.MESSAGE, number=1, message=timestamp.Timestamp,) + + status = proto.Field(proto.MESSAGE, number=2, message=gr_status.Status,) + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + mime_type = proto.Field(proto.STRING, number=3) + + knowledge_types = proto.RepeatedField(proto.ENUM, number=4, enum=KnowledgeType,) + + content_uri = proto.Field(proto.STRING, number=5, oneof="source") + + raw_content = proto.Field(proto.BYTES, number=9, oneof="source") + + enable_auto_reload = proto.Field(proto.BOOL, number=11) + + latest_reload_status = proto.Field(proto.MESSAGE, number=12, message=ReloadStatus,) + + metadata = proto.MapField(proto.STRING, proto.STRING, number=7) + + +class GetDocumentRequest(proto.Message): + r"""Request message for + [Documents.GetDocument][google.cloud.dialogflow.v2.Documents.GetDocument]. + + Attributes: + name (str): + Required. The name of the document to retrieve. Format + ``projects//locations//knowledgeBases//documents/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class ListDocumentsRequest(proto.Message): + r"""Request message for + [Documents.ListDocuments][google.cloud.dialogflow.v2.Documents.ListDocuments]. + + Attributes: + parent (str): + Required. The knowledge base to list all documents for. + Format: + ``projects//locations//knowledgeBases/``. + page_size (int): + The maximum number of items to return in a + single page. By default 10 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 ListDocumentsResponse(proto.Message): + r"""Response message for + [Documents.ListDocuments][google.cloud.dialogflow.v2.Documents.ListDocuments]. + + Attributes: + documents (Sequence[google.cloud.dialogflow_v2.types.Document]): + The list of documents. + 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 + + documents = proto.RepeatedField(proto.MESSAGE, number=1, message="Document",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class CreateDocumentRequest(proto.Message): + r"""Request message for + [Documents.CreateDocument][google.cloud.dialogflow.v2.Documents.CreateDocument]. + + Attributes: + parent (str): + Required. The knowledge base to create a document for. + Format: + ``projects//locations//knowledgeBases/``. + document (google.cloud.dialogflow_v2.types.Document): + Required. The document to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + document = proto.Field(proto.MESSAGE, number=2, message="Document",) + + +class DeleteDocumentRequest(proto.Message): + r"""Request message for + [Documents.DeleteDocument][google.cloud.dialogflow.v2.Documents.DeleteDocument]. + + Attributes: + name (str): + Required. The name of the document to delete. Format: + ``projects//locations//knowledgeBases//documents/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class UpdateDocumentRequest(proto.Message): + r"""Request message for + [Documents.UpdateDocument][google.cloud.dialogflow.v2.Documents.UpdateDocument]. + + Attributes: + document (google.cloud.dialogflow_v2.types.Document): + Required. The document to update. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Optional. Not specified means ``update all``. Currently, + only ``display_name`` can be updated, an InvalidArgument + will be returned for attempting to update other fields. + """ + + document = proto.Field(proto.MESSAGE, number=1, message="Document",) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class ReloadDocumentRequest(proto.Message): + r"""Request message for + [Documents.ReloadDocument][google.cloud.dialogflow.v2.Documents.ReloadDocument]. + + Attributes: + name (str): + Required. The name of the document to reload. Format: + ``projects//locations//knowledgeBases//documents/`` + content_uri (str): + Optional. The path of gcs source file for reloading document + content. For now, only gcs uri is supported. + + For documents stored in Google Cloud Storage, these URIs + must have the form ``gs:///``. + """ + + name = proto.Field(proto.STRING, number=1) + + content_uri = proto.Field(proto.STRING, number=3, oneof="source") + + +class KnowledgeOperationMetadata(proto.Message): + r"""Metadata in google::longrunning::Operation for Knowledge + operations. + + Attributes: + state (google.cloud.dialogflow_v2.types.KnowledgeOperationMetadata.State): + Output only. The current state of this + operation. + """ + + class State(proto.Enum): + r"""States of the operation.""" + STATE_UNSPECIFIED = 0 + PENDING = 1 + RUNNING = 2 + DONE = 3 + + state = proto.Field(proto.ENUM, number=1, enum=State,) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2/types/entity_type.py b/google/cloud/dialogflow_v2/types/entity_type.py index 156cb1609..3d0214e56 100644 --- a/google/cloud/dialogflow_v2/types/entity_type.py +++ b/google/cloud/dialogflow_v2/types/entity_type.py @@ -67,12 +67,12 @@ class EntityType(proto.Message): ``projects//agent/entityTypes/``. display_name (str): Required. The name of the entity type. - kind (~.gcd_entity_type.EntityType.Kind): + kind (google.cloud.dialogflow_v2.types.EntityType.Kind): Required. Indicates the kind of entity type. - auto_expansion_mode (~.gcd_entity_type.EntityType.AutoExpansionMode): + auto_expansion_mode (google.cloud.dialogflow_v2.types.EntityType.AutoExpansionMode): Optional. Indicates whether the entity type can be automatically expanded. - entities (Sequence[~.gcd_entity_type.EntityType.Entity]): + entities (Sequence[google.cloud.dialogflow_v2.types.EntityType.Entity]): Optional. The collection of entity entries associated with the entity type. enable_fuzzy_extraction (bool): @@ -178,7 +178,7 @@ class ListEntityTypesResponse(proto.Message): [EntityTypes.ListEntityTypes][google.cloud.dialogflow.v2.EntityTypes.ListEntityTypes]. Attributes: - entity_types (Sequence[~.gcd_entity_type.EntityType]): + entity_types (Sequence[google.cloud.dialogflow_v2.types.EntityType]): The list of agent entity types. There will be a maximum number of items returned based on the page_size field in the request. @@ -226,7 +226,7 @@ class CreateEntityTypeRequest(proto.Message): parent (str): Required. The agent to create a entity type for. Format: ``projects//agent``. - entity_type (~.gcd_entity_type.EntityType): + entity_type (google.cloud.dialogflow_v2.types.EntityType): Required. The entity type to create. language_code (str): Optional. The language used to access language-specific @@ -248,7 +248,7 @@ class UpdateEntityTypeRequest(proto.Message): [EntityTypes.UpdateEntityType][google.cloud.dialogflow.v2.EntityTypes.UpdateEntityType]. Attributes: - entity_type (~.gcd_entity_type.EntityType): + entity_type (google.cloud.dialogflow_v2.types.EntityType): Required. The entity type to update. language_code (str): Optional. The language used to access language-specific @@ -256,7 +256,7 @@ class UpdateEntityTypeRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ @@ -295,7 +295,7 @@ class BatchUpdateEntityTypesRequest(proto.Message): file format can either be a serialized proto (of EntityBatch type) or a JSON object. Note: The URI must start with "gs://". - entity_type_batch_inline (~.gcd_entity_type.EntityTypeBatch): + entity_type_batch_inline (google.cloud.dialogflow_v2.types.EntityTypeBatch): The collection of entity types to update or create. language_code (str): @@ -304,7 +304,7 @@ class BatchUpdateEntityTypesRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ @@ -329,7 +329,7 @@ class BatchUpdateEntityTypesResponse(proto.Message): [EntityTypes.BatchUpdateEntityTypes][google.cloud.dialogflow.v2.EntityTypes.BatchUpdateEntityTypes]. Attributes: - entity_types (Sequence[~.gcd_entity_type.EntityType]): + entity_types (Sequence[google.cloud.dialogflow_v2.types.EntityType]): The collection of updated or created entity types. """ @@ -364,7 +364,7 @@ class BatchCreateEntitiesRequest(proto.Message): Required. The name of the entity type to create entities in. Format: ``projects//agent/entityTypes/``. - entities (Sequence[~.gcd_entity_type.EntityType.Entity]): + entities (Sequence[google.cloud.dialogflow_v2.types.EntityType.Entity]): Required. The entities to create. language_code (str): Optional. The language used to access language-specific @@ -392,7 +392,7 @@ class BatchUpdateEntitiesRequest(proto.Message): Required. The name of the entity type to update or create entities in. Format: ``projects//agent/entityTypes/``. - entities (Sequence[~.gcd_entity_type.EntityType.Entity]): + entities (Sequence[google.cloud.dialogflow_v2.types.EntityType.Entity]): Required. The entities to update or create. language_code (str): Optional. The language used to access language-specific @@ -400,7 +400,7 @@ class BatchUpdateEntitiesRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ @@ -449,7 +449,7 @@ class EntityTypeBatch(proto.Message): types. Attributes: - entity_types (Sequence[~.gcd_entity_type.EntityType]): + entity_types (Sequence[google.cloud.dialogflow_v2.types.EntityType]): A collection of entity types. """ diff --git a/google/cloud/dialogflow_v2/types/environment.py b/google/cloud/dialogflow_v2/types/environment.py index 82fe9ded5..21f40d80e 100644 --- a/google/cloud/dialogflow_v2/types/environment.py +++ b/google/cloud/dialogflow_v2/types/environment.py @@ -63,11 +63,11 @@ class Environment(proto.Message): Optional. The agent version loaded into this environment. Format: ``projects//agent/versions/``. - state (~.environment.Environment.State): + state (google.cloud.dialogflow_v2.types.Environment.State): Output only. The state of this environment. This field is read-only, i.e., it cannot be set by create and update methods. - update_time (~.timestamp.Timestamp): + update_time (google.protobuf.timestamp_pb2.Timestamp): Output only. The last update time of this environment. This field is read-only, i.e., it cannot be set by create and update methods. @@ -126,7 +126,7 @@ class ListEnvironmentsResponse(proto.Message): [Environments.ListEnvironments][google.cloud.dialogflow.v2.Environments.ListEnvironments]. Attributes: - environments (Sequence[~.environment.Environment]): + environments (Sequence[google.cloud.dialogflow_v2.types.Environment]): The list of agent environments. There will be a maximum number of items returned based on the page_size field in the request. diff --git a/google/cloud/dialogflow_v2/types/gcs.py b/google/cloud/dialogflow_v2/types/gcs.py new file mode 100644 index 000000000..5f0961da2 --- /dev/null +++ b/google/cloud/dialogflow_v2/types/gcs.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + + +__protobuf__ = proto.module(package="google.cloud.dialogflow.v2", manifest={},) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2/types/human_agent_assistant_event.py b/google/cloud/dialogflow_v2/types/human_agent_assistant_event.py new file mode 100644 index 000000000..48fdc4371 --- /dev/null +++ b/google/cloud/dialogflow_v2/types/human_agent_assistant_event.py @@ -0,0 +1,55 @@ +# -*- 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.dialogflow_v2.types import participant as gcd_participant + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.v2", manifest={"HumanAgentAssistantEvent",}, +) + + +class HumanAgentAssistantEvent(proto.Message): + r"""Represents a notification sent to Cloud Pub/Sub subscribers + for human agent assistant events in a specific conversation. + + Attributes: + conversation (str): + The conversation this notification refers to. Format: + ``projects//conversations/``. + participant (str): + The participant that the suggestion is compiled for. Format: + ``projects//conversations//participants/``. + It will not be set in legacy workflow. + suggestion_results (Sequence[google.cloud.dialogflow_v2.types.SuggestionResult]): + The suggestion results payload that this + notification refers to. + """ + + conversation = proto.Field(proto.STRING, number=1) + + participant = proto.Field(proto.STRING, number=3) + + suggestion_results = proto.RepeatedField( + proto.MESSAGE, number=5, message=gcd_participant.SuggestionResult, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2/types/intent.py b/google/cloud/dialogflow_v2/types/intent.py index e16fd8942..5c7505576 100644 --- a/google/cloud/dialogflow_v2/types/intent.py +++ b/google/cloud/dialogflow_v2/types/intent.py @@ -74,7 +74,7 @@ class Intent(proto.Message): ``projects//agent/intents/``. display_name (str): Required. The name of this intent. - webhook_state (~.gcd_intent.Intent.WebhookState): + webhook_state (google.cloud.dialogflow_v2.types.Intent.WebhookState): Optional. Indicates whether webhooks are enabled for the intent. priority (int): @@ -95,6 +95,17 @@ class Intent(proto.Message): then this intent is not taken into account during inference in ``ML ONLY`` match mode. Also, auto-markup in the UI is turned off. + live_agent_handoff (bool): + Optional. Indicates that a live agent should be brought in + to handle the interaction with the user. In most cases, when + you set this flag to true, you would also want to set + end_interaction to true as well. Default is false. + end_interaction (bool): + Optional. Indicates that this intent ends an + interaction. Some integrations (e.g., Actions on + Google or Dialogflow phone gateway) use this + information to close interaction with an end + user. Default is false. input_context_names (Sequence[str]): Optional. The list of context names required for this intent to be triggered. Format: @@ -106,14 +117,14 @@ class Intent(proto.Message): be present in the active user session for an event to trigger this intent. Event names are limited to 150 characters. - training_phrases (Sequence[~.gcd_intent.Intent.TrainingPhrase]): + training_phrases (Sequence[google.cloud.dialogflow_v2.types.Intent.TrainingPhrase]): Optional. The collection of examples that the agent is trained on. action (str): Optional. The name of the action associated with the intent. Note: The action name must not contain whitespaces. - output_contexts (Sequence[~.context.Context]): + output_contexts (Sequence[google.cloud.dialogflow_v2.types.Context]): Optional. The collection of contexts that are activated when the intent is matched. Context messages in this collection should not set the parameters field. Setting the @@ -124,13 +135,13 @@ class Intent(proto.Message): Optional. Indicates whether to delete all contexts in the current session when this intent is matched. - parameters (Sequence[~.gcd_intent.Intent.Parameter]): + parameters (Sequence[google.cloud.dialogflow_v2.types.Intent.Parameter]): Optional. The collection of parameters associated with the intent. - messages (Sequence[~.gcd_intent.Intent.Message]): + messages (Sequence[google.cloud.dialogflow_v2.types.Intent.Message]): Optional. The collection of rich messages corresponding to the ``Response`` field in the Dialogflow console. - default_response_platforms (Sequence[~.gcd_intent.Intent.Message.Platform]): + default_response_platforms (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.Platform]): Optional. The list of platforms for which the first responses will be copied from the messages in PLATFORM_UNSPECIFIED (i.e. default platform). @@ -152,7 +163,7 @@ class Intent(proto.Message): It identifies the parent followup intent. Format: ``projects//agent/intents/``. - followup_intent_info (Sequence[~.gcd_intent.Intent.FollowupIntentInfo]): + followup_intent_info (Sequence[google.cloud.dialogflow_v2.types.Intent.FollowupIntentInfo]): Read-only. Information about all followup intents that have this intent as a direct or indirect parent. We populate this field only in @@ -172,9 +183,9 @@ class TrainingPhrase(proto.Message): name (str): Output only. The unique identifier of this training phrase. - type_ (~.gcd_intent.Intent.TrainingPhrase.Type): + type_ (google.cloud.dialogflow_v2.types.Intent.TrainingPhrase.Type): Required. The type of the training phrase. - parts (Sequence[~.gcd_intent.Intent.TrainingPhrase.Part]): + parts (Sequence[google.cloud.dialogflow_v2.types.Intent.TrainingPhrase.Part]): Required. The ordered list of training phrase parts. The parts are concatenated in order to form the training phrase. @@ -316,39 +327,39 @@ class Message(proto.Message): messages `__. Attributes: - text (~.gcd_intent.Intent.Message.Text): + text (google.cloud.dialogflow_v2.types.Intent.Message.Text): The text response. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2.types.Intent.Message.Image): The image response. - quick_replies (~.gcd_intent.Intent.Message.QuickReplies): + quick_replies (google.cloud.dialogflow_v2.types.Intent.Message.QuickReplies): The quick replies response. - card (~.gcd_intent.Intent.Message.Card): + card (google.cloud.dialogflow_v2.types.Intent.Message.Card): The card response. - payload (~.struct.Struct): + payload (google.protobuf.struct_pb2.Struct): A custom platform-specific response. - simple_responses (~.gcd_intent.Intent.Message.SimpleResponses): + simple_responses (google.cloud.dialogflow_v2.types.Intent.Message.SimpleResponses): The voice and text-only responses for Actions on Google. - basic_card (~.gcd_intent.Intent.Message.BasicCard): + basic_card (google.cloud.dialogflow_v2.types.Intent.Message.BasicCard): The basic card response for Actions on Google. - suggestions (~.gcd_intent.Intent.Message.Suggestions): + suggestions (google.cloud.dialogflow_v2.types.Intent.Message.Suggestions): The suggestion chips for Actions on Google. - link_out_suggestion (~.gcd_intent.Intent.Message.LinkOutSuggestion): + link_out_suggestion (google.cloud.dialogflow_v2.types.Intent.Message.LinkOutSuggestion): The link out suggestion chip for Actions on Google. - list_select (~.gcd_intent.Intent.Message.ListSelect): + list_select (google.cloud.dialogflow_v2.types.Intent.Message.ListSelect): The list card response for Actions on Google. - carousel_select (~.gcd_intent.Intent.Message.CarouselSelect): + carousel_select (google.cloud.dialogflow_v2.types.Intent.Message.CarouselSelect): The carousel card response for Actions on Google. - browse_carousel_card (~.gcd_intent.Intent.Message.BrowseCarouselCard): + browse_carousel_card (google.cloud.dialogflow_v2.types.Intent.Message.BrowseCarouselCard): Browse carousel card for Actions on Google. - table_card (~.gcd_intent.Intent.Message.TableCard): + table_card (google.cloud.dialogflow_v2.types.Intent.Message.TableCard): Table card for Actions on Google. - media_content (~.gcd_intent.Intent.Message.MediaContent): + media_content (google.cloud.dialogflow_v2.types.Intent.Message.MediaContent): The media content card for Actions on Google. - platform (~.gcd_intent.Intent.Message.Platform): + platform (google.cloud.dialogflow_v2.types.Intent.Message.Platform): Optional. The platform that this message is intended for. """ @@ -420,7 +431,7 @@ class Card(proto.Message): image_uri (str): Optional. The public URI to an image file for the card. - buttons (Sequence[~.gcd_intent.Intent.Message.Card.Button]): + buttons (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.Card.Button]): Optional. The collection of card buttons. """ @@ -477,7 +488,7 @@ class SimpleResponses(proto.Message): ``SimpleResponse``. Attributes: - simple_responses (Sequence[~.gcd_intent.Intent.Message.SimpleResponse]): + simple_responses (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.SimpleResponse]): Required. The list of simple responses. """ @@ -496,9 +507,9 @@ class BasicCard(proto.Message): formatted_text (str): Required, unless image is present. The body text of the card. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2.types.Intent.Message.Image): Optional. The image for the card. - buttons (Sequence[~.gcd_intent.Intent.Message.BasicCard.Button]): + buttons (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.BasicCard.Button]): Optional. The collection of card buttons. """ @@ -508,7 +519,7 @@ class Button(proto.Message): Attributes: title (str): Required. The title of the button. - open_uri_action (~.gcd_intent.Intent.Message.BasicCard.Button.OpenUriAction): + open_uri_action (google.cloud.dialogflow_v2.types.Intent.Message.BasicCard.Button.OpenUriAction): Required. Action to take when a user taps on the button. """ @@ -561,7 +572,7 @@ class Suggestions(proto.Message): r"""The collection of suggestions. Attributes: - suggestions (Sequence[~.gcd_intent.Intent.Message.Suggestion]): + suggestions (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.Suggestion]): Required. The list of suggested replies. """ @@ -592,7 +603,7 @@ class ListSelect(proto.Message): Attributes: title (str): Optional. The overall title of the list. - items (Sequence[~.gcd_intent.Intent.Message.ListSelect.Item]): + items (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.ListSelect.Item]): Required. List items. subtitle (str): Optional. Subtitle of the list. @@ -602,14 +613,14 @@ class Item(proto.Message): r"""An item in the list. Attributes: - info (~.gcd_intent.Intent.Message.SelectItemInfo): + info (google.cloud.dialogflow_v2.types.Intent.Message.SelectItemInfo): Required. Additional information about this option. title (str): Required. The title of the list item. description (str): Optional. The main text describing the item. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2.types.Intent.Message.Image): Optional. The image to display. """ @@ -637,7 +648,7 @@ class CarouselSelect(proto.Message): r"""The card for presenting a carousel of options to select from. Attributes: - items (Sequence[~.gcd_intent.Intent.Message.CarouselSelect.Item]): + items (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.CarouselSelect.Item]): Required. Carousel items. """ @@ -645,14 +656,14 @@ class Item(proto.Message): r"""An item in the carousel. Attributes: - info (~.gcd_intent.Intent.Message.SelectItemInfo): + info (google.cloud.dialogflow_v2.types.Intent.Message.SelectItemInfo): Required. Additional info about the option item. title (str): Required. Title of the carousel item. description (str): Optional. The body text of the card. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2.types.Intent.Message.Image): Optional. The image to display. """ @@ -693,10 +704,10 @@ class MediaContent(proto.Message): r"""The media content card for Actions on Google. Attributes: - media_type (~.gcd_intent.Intent.Message.MediaContent.ResponseMediaType): + media_type (google.cloud.dialogflow_v2.types.Intent.Message.MediaContent.ResponseMediaType): Optional. What type of media is the content (ie "audio"). - media_objects (Sequence[~.gcd_intent.Intent.Message.MediaContent.ResponseMediaObject]): + media_objects (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.MediaContent.ResponseMediaObject]): Required. List of media objects. """ @@ -713,10 +724,10 @@ class ResponseMediaObject(proto.Message): Required. Name of media card. description (str): Optional. Description of media card. - large_image (~.gcd_intent.Intent.Message.Image): + large_image (google.cloud.dialogflow_v2.types.Intent.Message.Image): Optional. Image to display above media content. - icon (~.gcd_intent.Intent.Message.Image): + icon (google.cloud.dialogflow_v2.types.Intent.Message.Image): Optional. Icon to display above media content. content_url (str): @@ -760,11 +771,11 @@ class BrowseCarouselCard(proto.Message): https://developers.google.com/actions/assistant/responses#browsing_carousel Attributes: - items (Sequence[~.gcd_intent.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem]): + items (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem]): Required. List of items in the Browse Carousel Card. Minimum of two items, maximum of ten. - image_display_options (~.gcd_intent.Intent.Message.BrowseCarouselCard.ImageDisplayOptions): + image_display_options (google.cloud.dialogflow_v2.types.Intent.Message.BrowseCarouselCard.ImageDisplayOptions): Optional. Settings for displaying the image. Applies to every image in [items][google.cloud.dialogflow.v2.Intent.Message.BrowseCarouselCard.items]. @@ -785,7 +796,7 @@ class BrowseCarouselCardItem(proto.Message): r"""Browsing carousel tile Attributes: - open_uri_action (~.gcd_intent.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem.OpenUrlAction): + open_uri_action (google.cloud.dialogflow_v2.types.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem.OpenUrlAction): Required. Action to present to the user. title (str): Required. Title of the carousel item. Maximum @@ -793,7 +804,7 @@ class BrowseCarouselCardItem(proto.Message): description (str): Optional. Description of the carousel item. Maximum of four lines of text. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2.types.Intent.Message.Image): Optional. Hero image for the carousel item. footer (str): Optional. Text that appears at the bottom of @@ -807,7 +818,7 @@ class OpenUrlAction(proto.Message): Attributes: url (str): Required. URL - url_type_hint (~.gcd_intent.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem.OpenUrlAction.UrlTypeHint): + url_type_hint (google.cloud.dialogflow_v2.types.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem.OpenUrlAction.UrlTypeHint): Optional. Specifies the type of viewer that is used when opening the URL. Defaults to opening via web browser. @@ -863,15 +874,15 @@ class TableCard(proto.Message): Required. Title of the card. subtitle (str): Optional. Subtitle to the title. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2.types.Intent.Message.Image): Optional. Image which should be displayed on the card. - column_properties (Sequence[~.gcd_intent.Intent.Message.ColumnProperties]): + column_properties (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.ColumnProperties]): Optional. Display properties for the columns in this table. - rows (Sequence[~.gcd_intent.Intent.Message.TableCardRow]): + rows (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.TableCardRow]): Optional. Rows in this table of data. - buttons (Sequence[~.gcd_intent.Intent.Message.BasicCard.Button]): + buttons (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.BasicCard.Button]): Optional. List of buttons for the card. """ @@ -902,7 +913,7 @@ class ColumnProperties(proto.Message): Attributes: header (str): Required. Column heading. - horizontal_alignment (~.gcd_intent.Intent.Message.ColumnProperties.HorizontalAlignment): + horizontal_alignment (google.cloud.dialogflow_v2.types.Intent.Message.ColumnProperties.HorizontalAlignment): Optional. Defines text alignment for all cells in this column. """ @@ -927,7 +938,7 @@ class TableCardRow(proto.Message): [TableCard][google.cloud.dialogflow.v2.Intent.Message.TableCard]. Attributes: - cells (Sequence[~.gcd_intent.Intent.Message.TableCardCell]): + cells (Sequence[google.cloud.dialogflow_v2.types.Intent.Message.TableCardCell]): Optional. List of cells that make up this row. divider_after (bool): @@ -1068,6 +1079,10 @@ class FollowupIntentInfo(proto.Message): ml_disabled = proto.Field(proto.BOOL, number=19) + live_agent_handoff = proto.Field(proto.BOOL, number=20) + + end_interaction = proto.Field(proto.BOOL, number=21) + input_context_names = proto.RepeatedField(proto.STRING, number=7) events = proto.RepeatedField(proto.STRING, number=8) @@ -1115,7 +1130,7 @@ class ListIntentsRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - intent_view (~.gcd_intent.IntentView): + intent_view (google.cloud.dialogflow_v2.types.IntentView): Optional. The resource view to apply to the returned intent. page_size (int): @@ -1143,7 +1158,7 @@ class ListIntentsResponse(proto.Message): [Intents.ListIntents][google.cloud.dialogflow.v2.Intents.ListIntents]. Attributes: - intents (Sequence[~.gcd_intent.Intent]): + intents (Sequence[google.cloud.dialogflow_v2.types.Intent]): The list of agent intents. There will be a maximum number of items returned based on the page_size field in the request. next_page_token (str): @@ -1175,7 +1190,7 @@ class GetIntentRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - intent_view (~.gcd_intent.IntentView): + intent_view (google.cloud.dialogflow_v2.types.IntentView): Optional. The resource view to apply to the returned intent. """ @@ -1195,7 +1210,7 @@ class CreateIntentRequest(proto.Message): parent (str): Required. The agent to create a intent for. Format: ``projects//agent``. - intent (~.gcd_intent.Intent): + intent (google.cloud.dialogflow_v2.types.Intent): Required. The intent to create. language_code (str): Optional. The language used to access language-specific @@ -1203,7 +1218,7 @@ class CreateIntentRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - intent_view (~.gcd_intent.IntentView): + intent_view (google.cloud.dialogflow_v2.types.IntentView): Optional. The resource view to apply to the returned intent. """ @@ -1222,7 +1237,7 @@ class UpdateIntentRequest(proto.Message): [Intents.UpdateIntent][google.cloud.dialogflow.v2.Intents.UpdateIntent]. Attributes: - intent (~.gcd_intent.Intent): + intent (google.cloud.dialogflow_v2.types.Intent): Required. The intent to update. language_code (str): Optional. The language used to access language-specific @@ -1230,10 +1245,10 @@ class UpdateIntentRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. - intent_view (~.gcd_intent.IntentView): + intent_view (google.cloud.dialogflow_v2.types.IntentView): Optional. The resource view to apply to the returned intent. """ @@ -1275,7 +1290,7 @@ class BatchUpdateIntentsRequest(proto.Message): format can either be a serialized proto (of IntentBatch type) or JSON object. Note: The URI must start with "gs://". - intent_batch_inline (~.gcd_intent.IntentBatch): + intent_batch_inline (google.cloud.dialogflow_v2.types.IntentBatch): The collection of intents to update or create. language_code (str): @@ -1284,10 +1299,10 @@ class BatchUpdateIntentsRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. - intent_view (~.gcd_intent.IntentView): + intent_view (google.cloud.dialogflow_v2.types.IntentView): Optional. The resource view to apply to the returned intent. """ @@ -1312,7 +1327,7 @@ class BatchUpdateIntentsResponse(proto.Message): [Intents.BatchUpdateIntents][google.cloud.dialogflow.v2.Intents.BatchUpdateIntents]. Attributes: - intents (Sequence[~.gcd_intent.Intent]): + intents (Sequence[google.cloud.dialogflow_v2.types.Intent]): The collection of updated or created intents. """ @@ -1327,7 +1342,7 @@ class BatchDeleteIntentsRequest(proto.Message): parent (str): Required. The name of the agent to delete all entities types for. Format: ``projects//agent``. - intents (Sequence[~.gcd_intent.Intent]): + intents (Sequence[google.cloud.dialogflow_v2.types.Intent]): Required. The collection of intents to delete. Only intent ``name`` must be filled in. """ @@ -1341,7 +1356,7 @@ class IntentBatch(proto.Message): r"""This message is a wrapper around a collection of intents. Attributes: - intents (Sequence[~.gcd_intent.Intent]): + intents (Sequence[google.cloud.dialogflow_v2.types.Intent]): A collection of intents. """ diff --git a/google/cloud/dialogflow_v2/types/knowledge_base.py b/google/cloud/dialogflow_v2/types/knowledge_base.py new file mode 100644 index 000000000..659be2ce6 --- /dev/null +++ b/google/cloud/dialogflow_v2/types/knowledge_base.py @@ -0,0 +1,189 @@ +# -*- 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.v2", + manifest={ + "KnowledgeBase", + "ListKnowledgeBasesRequest", + "ListKnowledgeBasesResponse", + "GetKnowledgeBaseRequest", + "CreateKnowledgeBaseRequest", + "DeleteKnowledgeBaseRequest", + "UpdateKnowledgeBaseRequest", + }, +) + + +class KnowledgeBase(proto.Message): + r"""A knowledge base represents a collection of knowledge documents that + you provide to Dialogflow. Your knowledge documents contain + information that may be useful during conversations with end-users. + Some Dialogflow features use knowledge bases when looking for a + response to an end-user input. + + For more information, see the `knowledge base + guide `__. + + Note: The ``projects.agent.knowledgeBases`` resource is deprecated; + only use ``projects.knowledgeBases``. + + Attributes: + name (str): + The knowledge base resource name. The name must be empty + when creating a knowledge base. Format: + ``projects//locations//knowledgeBases/``. + display_name (str): + Required. The display name of the knowledge + base. The name must be 1024 bytes or less; + otherwise, the creation request fails. + language_code (str): + Language which represents the KnowledgeBase. + When the KnowledgeBase is created/updated, + expect this to be present for non en-us + languages. When unspecified, the default + language code en-us applies. + """ + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + language_code = proto.Field(proto.STRING, number=4) + + +class ListKnowledgeBasesRequest(proto.Message): + r"""Request message for + [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBases.ListKnowledgeBases]. + + Attributes: + parent (str): + Required. The project to list of knowledge bases for. + Format: ``projects//locations/``. + page_size (int): + The maximum number of items to return in a + single page. By default 10 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 ListKnowledgeBasesResponse(proto.Message): + r"""Response message for + [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2.KnowledgeBases.ListKnowledgeBases]. + + Attributes: + knowledge_bases (Sequence[google.cloud.dialogflow_v2.types.KnowledgeBase]): + The list of knowledge bases. + 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 + + knowledge_bases = proto.RepeatedField( + proto.MESSAGE, number=1, message="KnowledgeBase", + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetKnowledgeBaseRequest(proto.Message): + r"""Request message for + [KnowledgeBases.GetKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.GetKnowledgeBase]. + + Attributes: + name (str): + Required. The name of the knowledge base to retrieve. Format + ``projects//locations//knowledgeBases/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreateKnowledgeBaseRequest(proto.Message): + r"""Request message for + [KnowledgeBases.CreateKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.CreateKnowledgeBase]. + + Attributes: + parent (str): + Required. The project to create a knowledge base for. + Format: ``projects//locations/``. + knowledge_base (google.cloud.dialogflow_v2.types.KnowledgeBase): + Required. The knowledge base to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + knowledge_base = proto.Field(proto.MESSAGE, number=2, message="KnowledgeBase",) + + +class DeleteKnowledgeBaseRequest(proto.Message): + r"""Request message for + [KnowledgeBases.DeleteKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.DeleteKnowledgeBase]. + + Attributes: + name (str): + Required. The name of the knowledge base to delete. Format: + ``projects//locations//knowledgeBases/``. + force (bool): + Optional. Force deletes the knowledge base. + When set to true, any documents in the knowledge + base are also deleted. + """ + + name = proto.Field(proto.STRING, number=1) + + force = proto.Field(proto.BOOL, number=2) + + +class UpdateKnowledgeBaseRequest(proto.Message): + r"""Request message for + [KnowledgeBases.UpdateKnowledgeBase][google.cloud.dialogflow.v2.KnowledgeBases.UpdateKnowledgeBase]. + + Attributes: + knowledge_base (google.cloud.dialogflow_v2.types.KnowledgeBase): + Required. The knowledge base to update. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Optional. Not specified means ``update all``. Currently, + only ``display_name`` can be updated, an InvalidArgument + will be returned for attempting to update other fields. + """ + + knowledge_base = proto.Field(proto.MESSAGE, number=1, message="KnowledgeBase",) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2/types/participant.py b/google/cloud/dialogflow_v2/types/participant.py new file mode 100644 index 000000000..1be79ddc0 --- /dev/null +++ b/google/cloud/dialogflow_v2/types/participant.py @@ -0,0 +1,932 @@ +# -*- 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.dialogflow_v2.types import audio_config as gcd_audio_config +from google.cloud.dialogflow_v2.types import session +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 google.rpc import status_pb2 as status # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.v2", + manifest={ + "Participant", + "Message", + "CreateParticipantRequest", + "GetParticipantRequest", + "ListParticipantsRequest", + "ListParticipantsResponse", + "UpdateParticipantRequest", + "AnalyzeContentRequest", + "DtmfParameters", + "AnalyzeContentResponse", + "StreamingAnalyzeContentRequest", + "StreamingAnalyzeContentResponse", + "SuggestArticlesRequest", + "SuggestArticlesResponse", + "SuggestFaqAnswersRequest", + "SuggestFaqAnswersResponse", + "AudioInput", + "OutputAudio", + "AutomatedAgentReply", + "ArticleAnswer", + "FaqAnswer", + "SuggestionResult", + "InputTextConfig", + "AnnotatedMessagePart", + "MessageAnnotation", + }, +) + + +class Participant(proto.Message): + r"""Represents a conversation participant (human agent, virtual + agent, end-user). + + Attributes: + name (str): + Optional. The unique identifier of this participant. Format: + ``projects//locations//conversations//participants/``. + role (google.cloud.dialogflow_v2.types.Participant.Role): + Immutable. The role this participant plays in + the conversation. This field must be set during + participant creation and is then immutable. + sip_recording_media_label (str): + Optional. Label applied to streams + representing this participant in SIPREC XML + metadata and SDP. This is used to assign + transcriptions from that media stream to this + participant. This field can be updated. + """ + + class Role(proto.Enum): + r"""Enumeration of the roles a participant can play in a + conversation. + """ + ROLE_UNSPECIFIED = 0 + HUMAN_AGENT = 1 + AUTOMATED_AGENT = 2 + END_USER = 3 + + name = proto.Field(proto.STRING, number=1) + + role = proto.Field(proto.ENUM, number=2, enum=Role,) + + sip_recording_media_label = proto.Field(proto.STRING, number=6) + + +class Message(proto.Message): + r"""Represents a message posted into a conversation. + + Attributes: + name (str): + The unique identifier of the message. Format: + ``projects//locations//conversations//messages/``. + content (str): + Required. The message content. + language_code (str): + Optional. The message language. This should be a + `BCP-47 `__ + language tag. Example: "en-US". + participant (str): + Output only. The participant that sends this + message. + participant_role (google.cloud.dialogflow_v2.types.Participant.Role): + Output only. The role of the participant. + create_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The time when the message was + created. + message_annotation (google.cloud.dialogflow_v2.types.MessageAnnotation): + Output only. The annotation for the message. + """ + + name = proto.Field(proto.STRING, number=1) + + content = proto.Field(proto.STRING, number=2) + + language_code = proto.Field(proto.STRING, number=3) + + participant = proto.Field(proto.STRING, number=4) + + participant_role = proto.Field(proto.ENUM, number=5, enum="Participant.Role",) + + create_time = proto.Field(proto.MESSAGE, number=6, message=timestamp.Timestamp,) + + message_annotation = proto.Field( + proto.MESSAGE, number=7, message="MessageAnnotation", + ) + + +class CreateParticipantRequest(proto.Message): + r"""The request message for + [Participants.CreateParticipant][google.cloud.dialogflow.v2.Participants.CreateParticipant]. + + Attributes: + parent (str): + Required. Resource identifier of the conversation adding the + participant. Format: + ``projects//locations//conversations/``. + participant (google.cloud.dialogflow_v2.types.Participant): + Required. The participant to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + participant = proto.Field(proto.MESSAGE, number=2, message="Participant",) + + +class GetParticipantRequest(proto.Message): + r"""The request message for + [Participants.GetParticipant][google.cloud.dialogflow.v2.Participants.GetParticipant]. + + Attributes: + name (str): + Required. The name of the participant. Format: + ``projects//locations//conversations//participants/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class ListParticipantsRequest(proto.Message): + r"""The request message for + [Participants.ListParticipants][google.cloud.dialogflow.v2.Participants.ListParticipants]. + + Attributes: + parent (str): + Required. The conversation to list all participants from. + Format: + ``projects//locations//conversations/``. + page_size (int): + Optional. The maximum number of items to + return in a single page. By default 100 and at + most 1000. + page_token (str): + Optional. 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 ListParticipantsResponse(proto.Message): + r"""The response message for + [Participants.ListParticipants][google.cloud.dialogflow.v2.Participants.ListParticipants]. + + Attributes: + participants (Sequence[google.cloud.dialogflow_v2.types.Participant]): + The list of participants. There is 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 + + participants = proto.RepeatedField(proto.MESSAGE, number=1, message="Participant",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class UpdateParticipantRequest(proto.Message): + r"""The request message for + [Participants.UpdateParticipant][google.cloud.dialogflow.v2.Participants.UpdateParticipant]. + + Attributes: + participant (google.cloud.dialogflow_v2.types.Participant): + Required. The participant to update. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. The mask to specify which fields to + update. + """ + + participant = proto.Field(proto.MESSAGE, number=1, message="Participant",) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class AnalyzeContentRequest(proto.Message): + r"""The request message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent]. + + Attributes: + participant (str): + Required. The name of the participant this text comes from. + Format: + ``projects//locations//conversations//participants/``. + text_input (google.cloud.dialogflow_v2.types.TextInput): + The natural language text to be processed. + audio_input (google.cloud.dialogflow_v2.types.AudioInput): + The natural language speech audio to be + processed. + event_input (google.cloud.dialogflow_v2.types.EventInput): + An input event to send to Dialogflow. + reply_audio_config (google.cloud.dialogflow_v2.types.OutputAudioConfig): + Speech synthesis configuration. + The speech synthesis settings for a virtual + agent that may be configured for the associated + conversation profile are not used when calling + AnalyzeContent. If this configuration is not + supplied, speech synthesis is disabled. + query_params (google.cloud.dialogflow_v2.types.QueryParameters): + Parameters for a Dialogflow virtual-agent + query. + request_id (str): + A unique identifier for this request. Restricted to 36 ASCII + characters. A random UUID is recommended. This request is + only idempotent if a ``request_id`` is provided. + """ + + participant = proto.Field(proto.STRING, number=1) + + text_input = proto.Field( + proto.MESSAGE, number=6, oneof="input", message=session.TextInput, + ) + + audio_input = proto.Field( + proto.MESSAGE, number=7, oneof="input", message="AudioInput", + ) + + event_input = proto.Field( + proto.MESSAGE, number=8, oneof="input", message=session.EventInput, + ) + + reply_audio_config = proto.Field( + proto.MESSAGE, number=5, message=gcd_audio_config.OutputAudioConfig, + ) + + query_params = proto.Field( + proto.MESSAGE, number=9, message=session.QueryParameters, + ) + + request_id = proto.Field(proto.STRING, number=11) + + +class DtmfParameters(proto.Message): + r"""The message in the response that indicates the parameters of + DTMF. + + Attributes: + accepts_dtmf_input (bool): + Indicates whether DTMF input can be handled + in the next request. + """ + + accepts_dtmf_input = proto.Field(proto.BOOL, number=1) + + +class AnalyzeContentResponse(proto.Message): + r"""The response message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent]. + + Attributes: + reply_text (str): + The output text content. + This field is set if the automated agent + responded with text to show to the user. + reply_audio (google.cloud.dialogflow_v2.types.OutputAudio): + The audio data bytes encoded as specified in the request. + This field is set if: + + - ``reply_audio_config`` was specified in the request, or + - The automated agent responded with audio to play to the + user. In such case, ``reply_audio.config`` contains + settings used to synthesize the speech. + + 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. + automated_agent_reply (google.cloud.dialogflow_v2.types.AutomatedAgentReply): + Only set if a Dialogflow automated agent has responded. Note + that: + [AutomatedAgentReply.detect_intent_response.output_audio][] + and + [AutomatedAgentReply.detect_intent_response.output_audio_config][] + are always empty, use + [reply_audio][google.cloud.dialogflow.v2.AnalyzeContentResponse.reply_audio] + instead. + message (google.cloud.dialogflow_v2.types.Message): + Message analyzed by CCAI. + human_agent_suggestion_results (Sequence[google.cloud.dialogflow_v2.types.SuggestionResult]): + The suggestions for most recent human agent. The order is + the same as + [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] + of + [HumanAgentAssistantConfig.human_agent_suggestion_config][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.human_agent_suggestion_config]. + end_user_suggestion_results (Sequence[google.cloud.dialogflow_v2.types.SuggestionResult]): + The suggestions for end user. The order is the same as + [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] + of + [HumanAgentAssistantConfig.end_user_suggestion_config][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.end_user_suggestion_config]. + dtmf_parameters (google.cloud.dialogflow_v2.types.DtmfParameters): + Indicates the parameters of DTMF. + """ + + reply_text = proto.Field(proto.STRING, number=1) + + reply_audio = proto.Field(proto.MESSAGE, number=2, message="OutputAudio",) + + automated_agent_reply = proto.Field( + proto.MESSAGE, number=3, message="AutomatedAgentReply", + ) + + message = proto.Field(proto.MESSAGE, number=5, message="Message",) + + human_agent_suggestion_results = proto.RepeatedField( + proto.MESSAGE, number=6, message="SuggestionResult", + ) + + end_user_suggestion_results = proto.RepeatedField( + proto.MESSAGE, number=7, message="SuggestionResult", + ) + + dtmf_parameters = proto.Field(proto.MESSAGE, number=9, message="DtmfParameters",) + + +class StreamingAnalyzeContentRequest(proto.Message): + r"""The top-level message sent by the client to the + [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2.Participants.StreamingAnalyzeContent] + method. + + Multiple request messages should be sent in order: + + 1. The first message must contain + [participant][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.participant], + [config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] + and optionally + [query_params][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.query_params]. + If you want to receive an audio response, it should also contain + [reply_audio_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.reply_audio_config]. + The message must not contain + [input][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input]. + + 2. If + [config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] + in the first message was set to + [audio_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.audio_config], + all subsequent messages must contain + [input_audio][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input_audio] + to continue with Speech recognition. However, note that: + + - Dialogflow will bill you for the audio so far. + - Dialogflow discards all Speech recognition results in favor of + the text input. + + 3. If + [StreamingAnalyzeContentRequest.config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.config] + in the first message was set to + [StreamingAnalyzeContentRequest.text_config][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.text_config], + then the second message must contain only + [input_text][google.cloud.dialogflow.v2.StreamingAnalyzeContentRequest.input_text]. + Moreover, you must not send more than two messages. + + After you sent all input, you must half-close or abort the request + stream. + + Attributes: + participant (str): + Required. The name of the participant this text comes from. + Format: + ``projects//locations//conversations//participants/``. + audio_config (google.cloud.dialogflow_v2.types.InputAudioConfig): + Instructs the speech recognizer how to + process the speech audio. + text_config (google.cloud.dialogflow_v2.types.InputTextConfig): + The natural language text to be processed. + reply_audio_config (google.cloud.dialogflow_v2.types.OutputAudioConfig): + Speech synthesis configuration. + The speech synthesis settings for a virtual + agent that may be configured for the associated + conversation profile are not used when calling + StreamingAnalyzeContent. If this configuration + is not supplied, speech synthesis is disabled. + input_audio (bytes): + The input audio content to be recognized. Must be sent if + ``audio_config`` is set in the first message. The complete + audio over all streaming messages must not exceed 1 minute. + input_text (str): + The UTF-8 encoded natural language text to be processed. + Must be sent if ``text_config`` is set in the first message. + Text length must not exceed 256 bytes. The ``input_text`` + field can be only sent once. + input_dtmf (google.cloud.dialogflow_v2.types.TelephonyDtmfEvents): + The DTMF digits used to invoke intent and + fill in parameter value. + This input is ignored if the previous response + indicated that DTMF input is not accepted. + query_params (google.cloud.dialogflow_v2.types.QueryParameters): + Parameters for a Dialogflow virtual-agent + query. + """ + + participant = proto.Field(proto.STRING, number=1) + + audio_config = proto.Field( + proto.MESSAGE, + number=2, + oneof="config", + message=gcd_audio_config.InputAudioConfig, + ) + + text_config = proto.Field( + proto.MESSAGE, number=3, oneof="config", message="InputTextConfig", + ) + + reply_audio_config = proto.Field( + proto.MESSAGE, number=4, message=gcd_audio_config.OutputAudioConfig, + ) + + input_audio = proto.Field(proto.BYTES, number=5, oneof="input") + + input_text = proto.Field(proto.STRING, number=6, oneof="input") + + input_dtmf = proto.Field( + proto.MESSAGE, + number=9, + oneof="input", + message=gcd_audio_config.TelephonyDtmfEvents, + ) + + query_params = proto.Field( + proto.MESSAGE, number=7, message=session.QueryParameters, + ) + + +class StreamingAnalyzeContentResponse(proto.Message): + r"""The top-level message returned from the ``StreamingAnalyzeContent`` + 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 next message contains ``reply_text`` and optionally + ``reply_audio`` returned by an agent. This message may also + contain ``automated_agent_reply``. + + Attributes: + recognition_result (google.cloud.dialogflow_v2.types.StreamingRecognitionResult): + The result of speech recognition. + reply_text (str): + The output text content. + This field is set if an automated agent + responded with a text for the user. + reply_audio (google.cloud.dialogflow_v2.types.OutputAudio): + The audio data bytes encoded as specified in the request. + This field is set if: + + - The ``reply_audio_config`` field is specified in the + request. + - The automated agent, which this output comes from, + responded with audio. In such case, the + ``reply_audio.config`` field contains settings used to + synthesize the speech. + + 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. + automated_agent_reply (google.cloud.dialogflow_v2.types.AutomatedAgentReply): + Only set if a Dialogflow automated agent has responded. Note + that: + [AutomatedAgentReply.detect_intent_response.output_audio][] + and + [AutomatedAgentReply.detect_intent_response.output_audio_config][] + are always empty, use + [reply_audio][google.cloud.dialogflow.v2.StreamingAnalyzeContentResponse.reply_audio] + instead. + message (google.cloud.dialogflow_v2.types.Message): + Message analyzed by CCAI. + human_agent_suggestion_results (Sequence[google.cloud.dialogflow_v2.types.SuggestionResult]): + The suggestions for most recent human agent. The order is + the same as + [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] + of + [HumanAgentAssistantConfig.human_agent_suggestion_config][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.human_agent_suggestion_config]. + end_user_suggestion_results (Sequence[google.cloud.dialogflow_v2.types.SuggestionResult]): + The suggestions for end user. The order is the same as + [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] + of + [HumanAgentAssistantConfig.end_user_suggestion_config][google.cloud.dialogflow.v2.HumanAgentAssistantConfig.end_user_suggestion_config]. + dtmf_parameters (google.cloud.dialogflow_v2.types.DtmfParameters): + Indicates the parameters of DTMF. + """ + + recognition_result = proto.Field( + proto.MESSAGE, number=1, message=session.StreamingRecognitionResult, + ) + + reply_text = proto.Field(proto.STRING, number=2) + + reply_audio = proto.Field(proto.MESSAGE, number=3, message="OutputAudio",) + + automated_agent_reply = proto.Field( + proto.MESSAGE, number=4, message="AutomatedAgentReply", + ) + + message = proto.Field(proto.MESSAGE, number=6, message="Message",) + + human_agent_suggestion_results = proto.RepeatedField( + proto.MESSAGE, number=7, message="SuggestionResult", + ) + + end_user_suggestion_results = proto.RepeatedField( + proto.MESSAGE, number=8, message="SuggestionResult", + ) + + dtmf_parameters = proto.Field(proto.MESSAGE, number=10, message="DtmfParameters",) + + +class SuggestArticlesRequest(proto.Message): + r"""The request message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2.Participants.SuggestArticles]. + + Attributes: + parent (str): + Required. The name of the participant to fetch suggestion + for. Format: + ``projects//locations//conversations//participants/``. + latest_message (str): + The name of the latest conversation message to compile + suggestion for. If empty, it will be the latest message of + the conversation. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Max number of messages prior to and including + [latest_message][google.cloud.dialogflow.v2.SuggestArticlesRequest.latest_message] + to use as context when compiling the suggestion. By default + 20 and at most 50. + """ + + parent = proto.Field(proto.STRING, number=1) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class SuggestArticlesResponse(proto.Message): + r"""The response message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2.Participants.SuggestArticles]. + + Attributes: + article_answers (Sequence[google.cloud.dialogflow_v2.types.ArticleAnswer]): + Articles ordered by score in descending + order. + latest_message (str): + The name of the latest conversation message used to compile + suggestion for. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Number of messages prior to and including + [latest_message][google.cloud.dialogflow.v2.SuggestArticlesResponse.latest_message] + to compile the suggestion. It may be smaller than the + [SuggestArticlesRequest.context_size][google.cloud.dialogflow.v2.SuggestArticlesRequest.context_size] + field in the request if there aren't that many messages in + the conversation. + """ + + article_answers = proto.RepeatedField( + proto.MESSAGE, number=1, message="ArticleAnswer", + ) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class SuggestFaqAnswersRequest(proto.Message): + r"""The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2.Participants.SuggestFaqAnswers]. + + Attributes: + parent (str): + Required. The name of the participant to fetch suggestion + for. Format: + ``projects//locations//conversations//participants/``. + latest_message (str): + The name of the latest conversation message to compile + suggestion for. If empty, it will be the latest message of + the conversation. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Max number of messages prior to and including + [latest_message] to use as context when compiling the + suggestion. By default 20 and at most 50. + """ + + parent = proto.Field(proto.STRING, number=1) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class SuggestFaqAnswersResponse(proto.Message): + r"""The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2.Participants.SuggestFaqAnswers]. + + Attributes: + faq_answers (Sequence[google.cloud.dialogflow_v2.types.FaqAnswer]): + Answers extracted from FAQ documents. + latest_message (str): + The name of the latest conversation message used to compile + suggestion for. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Number of messages prior to and including + [latest_message][google.cloud.dialogflow.v2.SuggestFaqAnswersResponse.latest_message] + to compile the suggestion. It may be smaller than the + [SuggestFaqAnswersRequest.context_size][google.cloud.dialogflow.v2.SuggestFaqAnswersRequest.context_size] + field in the request if there aren't that many messages in + the conversation. + """ + + faq_answers = proto.RepeatedField(proto.MESSAGE, number=1, message="FaqAnswer",) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class AudioInput(proto.Message): + r"""Represents the natural language speech audio to be processed. + + Attributes: + config (google.cloud.dialogflow_v2.types.InputAudioConfig): + Required. Instructs the speech recognizer how + to process the speech audio. + audio (bytes): + Required. The natural language speech audio + to be processed. A single request can contain up + to 1 minute of speech audio data. The + transcribed text cannot contain more than 256 + bytes. + """ + + config = proto.Field( + proto.MESSAGE, number=1, message=gcd_audio_config.InputAudioConfig, + ) + + audio = proto.Field(proto.BYTES, number=2) + + +class OutputAudio(proto.Message): + r"""Represents the natural language speech audio to be played to + the end user. + + Attributes: + config (google.cloud.dialogflow_v2.types.OutputAudioConfig): + Instructs the speech synthesizer how to + generate the speech audio. + audio (bytes): + The natural language speech audio. + """ + + config = proto.Field( + proto.MESSAGE, number=1, message=gcd_audio_config.OutputAudioConfig, + ) + + audio = proto.Field(proto.BYTES, number=2) + + +class AutomatedAgentReply(proto.Message): + r"""Represents a response from an automated agent. + + Attributes: + detect_intent_response (google.cloud.dialogflow_v2.types.DetectIntentResponse): + Response of the Dialogflow + [Sessions.DetectIntent][google.cloud.dialogflow.v2.Sessions.DetectIntent] + call. + """ + + detect_intent_response = proto.Field( + proto.MESSAGE, number=1, message=session.DetectIntentResponse, + ) + + +class ArticleAnswer(proto.Message): + r"""Represents article answer. + + Attributes: + title (str): + The article title. + uri (str): + The article URI. + snippets (Sequence[str]): + Article snippets. + confidence (float): + Article match confidence. + The system's confidence score that this article + is a good match for this conversation, as a + value from 0.0 (completely uncertain) to 1.0 + (completely certain). + metadata (Sequence[google.cloud.dialogflow_v2.types.ArticleAnswer.MetadataEntry]): + A map that contains metadata about the answer + and the document from which it originates. + answer_record (str): + The name of answer record, in the format of + "projects//locations//answerRecords/". + """ + + title = proto.Field(proto.STRING, number=1) + + uri = proto.Field(proto.STRING, number=2) + + snippets = proto.RepeatedField(proto.STRING, number=3) + + confidence = proto.Field(proto.FLOAT, number=4) + + metadata = proto.MapField(proto.STRING, proto.STRING, number=5) + + answer_record = proto.Field(proto.STRING, number=6) + + +class FaqAnswer(proto.Message): + r"""Represents answer from "frequently asked questions". + + Attributes: + answer (str): + The piece of text from the ``source`` knowledge base + document. + confidence (float): + The system's confidence score that this + Knowledge answer is a good match for this + conversational query, range from 0.0 (completely + uncertain) to 1.0 (completely certain). + question (str): + The corresponding FAQ question. + source (str): + Indicates which Knowledge Document this answer was extracted + from. Format: + ``projects//locations//agent/knowledgeBases//documents/``. + metadata (Sequence[google.cloud.dialogflow_v2.types.FaqAnswer.MetadataEntry]): + A map that contains metadata about the answer + and the document from which it originates. + answer_record (str): + The name of answer record, in the format of + "projects//locations//answerRecords/". + """ + + answer = proto.Field(proto.STRING, number=1) + + confidence = proto.Field(proto.FLOAT, number=2) + + question = proto.Field(proto.STRING, number=3) + + source = proto.Field(proto.STRING, number=4) + + metadata = proto.MapField(proto.STRING, proto.STRING, number=5) + + answer_record = proto.Field(proto.STRING, number=6) + + +class SuggestionResult(proto.Message): + r"""One response of different type of suggestion response which is used + in the response of + [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent] + and + [Participants.AnalyzeContent][google.cloud.dialogflow.v2.Participants.AnalyzeContent], + as well as + [HumanAgentAssistantEvent][google.cloud.dialogflow.v2.HumanAgentAssistantEvent]. + + Attributes: + error (google.rpc.status_pb2.Status): + Error status if the request failed. + suggest_articles_response (google.cloud.dialogflow_v2.types.SuggestArticlesResponse): + SuggestArticlesResponse if request is for + ARTICLE_SUGGESTION. + suggest_faq_answers_response (google.cloud.dialogflow_v2.types.SuggestFaqAnswersResponse): + SuggestFaqAnswersResponse if request is for FAQ_ANSWER. + """ + + error = proto.Field( + proto.MESSAGE, number=1, oneof="suggestion_response", message=status.Status, + ) + + suggest_articles_response = proto.Field( + proto.MESSAGE, + number=2, + oneof="suggestion_response", + message="SuggestArticlesResponse", + ) + + suggest_faq_answers_response = proto.Field( + proto.MESSAGE, + number=3, + oneof="suggestion_response", + message="SuggestFaqAnswersResponse", + ) + + +class InputTextConfig(proto.Message): + r"""Defines the language used in the input text. + + Attributes: + language_code (str): + Required. The language of this conversational query. See + `Language + Support `__ + for a list of the currently supported language codes. + """ + + language_code = proto.Field(proto.STRING, number=1) + + +class AnnotatedMessagePart(proto.Message): + r"""Represents a part of a message possibly annotated with an + entity. The part can be an entity or purely a part of the + message between two entities or message start/end. + + Attributes: + text (str): + A part of a message possibly annotated with + an entity. + entity_type (str): + The `Dialogflow system entity + type `__ + of this message part. If this is empty, Dialogflow could not + annotate the phrase part with a system entity. + formatted_value (google.protobuf.struct_pb2.Value): + The `Dialogflow system entity formatted + value `__ + of this message part. For example for a system entity of + type ``@sys.unit-currency``, this may contain: + + .. raw:: html + +
+                {
+                  "amount": 5,
+                  "currency": "USD"
+                }
+                
+ """ + + text = proto.Field(proto.STRING, number=1) + + entity_type = proto.Field(proto.STRING, number=2) + + formatted_value = proto.Field(proto.MESSAGE, number=3, message=struct.Value,) + + +class MessageAnnotation(proto.Message): + r"""Represents the result of annotation for the message. + + Attributes: + parts (Sequence[google.cloud.dialogflow_v2.types.AnnotatedMessagePart]): + The collection of annotated message parts ordered by their + position in the message. You can recover the annotated + message by concatenating [AnnotatedMessagePart.text]. + contain_entities (bool): + Indicates whether the text message contains + entities. + """ + + parts = proto.RepeatedField( + proto.MESSAGE, number=1, message="AnnotatedMessagePart", + ) + + contain_entities = proto.Field(proto.BOOL, number=2) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2/types/session.py b/google/cloud/dialogflow_v2/types/session.py index ee2a9be77..f7bebda23 100644 --- a/google/cloud/dialogflow_v2/types/session.py +++ b/google/cloud/dialogflow_v2/types/session.py @@ -59,12 +59,14 @@ class DetectIntentRequest(proto.Message): ``projects//agent/sessions/``, or ``projects//agent/environments//users//sessions/``. If ``Environment ID`` is not specified, we assume default - 'draft' environment. If ``User ID`` is not specified, we are - using "-". It's up to the API caller to choose an - appropriate ``Session ID`` and ``User Id``. They can be a - random number or some type of user and session identifiers - (preferably hashed). The length of the ``Session ID`` and - ``User ID`` must not exceed 36 characters. + 'draft' environment (``Environment ID`` might be referred to + as environment name at some places). If ``User ID`` is not + specified, we are using "-". It's up to the API caller to + choose an appropriate ``Session ID`` and ``User Id``. They + can be a random number or some type of user and session + identifiers (preferably hashed). The length of the + ``Session ID`` and ``User ID`` must not exceed 36 + characters. For more information, see the `API interactions guide `__. @@ -72,9 +74,9 @@ class DetectIntentRequest(proto.Message): Note: Always use agent versions for production traffic. See `Versions and environments `__. - query_params (~.gcd_session.QueryParameters): + query_params (google.cloud.dialogflow_v2.types.QueryParameters): The parameters of this query. - query_input (~.gcd_session.QueryInput): + query_input (google.cloud.dialogflow_v2.types.QueryInput): Required. The input specification. It can be set to: 1. an audio config @@ -84,12 +86,12 @@ class DetectIntentRequest(proto.Message): or 3. an event that specifies which intent to trigger. - output_audio_config (~.gcd_audio_config.OutputAudioConfig): + output_audio_config (google.cloud.dialogflow_v2.types.OutputAudioConfig): Instructs the speech synthesizer how to generate the output audio. If this field is not set and agent-level speech synthesizer is not configured, no output audio is generated. - output_audio_config_mask (~.field_mask.FieldMask): + output_audio_config_mask (google.protobuf.field_mask_pb2.FieldMask): Mask for [output_audio_config][google.cloud.dialogflow.v2.DetectIntentRequest.output_audio_config] indicating which settings in this request-level config @@ -131,11 +133,11 @@ class DetectIntentResponse(proto.Message): 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 (~.gcd_session.QueryResult): + query_result (google.cloud.dialogflow_v2.types.QueryResult): The selected results of the conversational query or event processing. See ``alternative_query_results`` for additional potential results. - webhook_status (~.status.Status): + webhook_status (google.rpc.status_pb2.Status): Specifies the status of the webhook request. output_audio (bytes): The audio data bytes encoded as specified in the request. @@ -149,7 +151,7 @@ class DetectIntentResponse(proto.Message): 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 (~.gcd_audio_config.OutputAudioConfig): + output_audio_config (google.cloud.dialogflow_v2.types.OutputAudioConfig): The config used by the speech synthesizer to generate the output audio. """ @@ -176,32 +178,32 @@ class QueryParameters(proto.Message): zone database `__, e.g., America/New_York, Europe/Paris. If not provided, the time zone specified in agent settings is used. - geo_location (~.latlng.LatLng): + geo_location (google.type.latlng_pb2.LatLng): The geo location of this conversational query. - contexts (Sequence[~.context.Context]): + contexts (Sequence[google.cloud.dialogflow_v2.types.Context]): The collection of contexts to be activated before this query is executed. reset_contexts (bool): Specifies whether to delete all contexts in the current session before the new ones are activated. - session_entity_types (Sequence[~.session_entity_type.SessionEntityType]): + session_entity_types (Sequence[google.cloud.dialogflow_v2.types.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): + payload (google.protobuf.struct_pb2.Struct): This field can be used to pass custom data to your webhook. Arbitrary JSON objects are supported. If supplied, the value is used to populate the ``WebhookRequest.original_detect_intent_request.payload`` field sent to your webhook. - sentiment_analysis_request_config (~.gcd_session.SentimentAnalysisRequestConfig): + sentiment_analysis_request_config (google.cloud.dialogflow_v2.types.SentimentAnalysisRequestConfig): Configures the type of sentiment analysis to perform. If not provided, sentiment analysis is not performed. - webhook_headers (Sequence[~.gcd_session.QueryParameters.WebhookHeadersEntry]): + webhook_headers (Sequence[google.cloud.dialogflow_v2.types.QueryParameters.WebhookHeadersEntry]): This field can be used to pass HTTP headers for a webhook call. These headers will be sent to webhook along with the headers that have been @@ -247,12 +249,12 @@ class QueryInput(proto.Message): 3. An event that specifies which intent to trigger. Attributes: - audio_config (~.gcd_audio_config.InputAudioConfig): + audio_config (google.cloud.dialogflow_v2.types.InputAudioConfig): Instructs the speech recognizer how to process the speech audio. - text (~.gcd_session.TextInput): + text (google.cloud.dialogflow_v2.types.TextInput): The natural language text to be processed. - event (~.gcd_session.EventInput): + event (google.cloud.dialogflow_v2.types.EventInput): The event to be processed. """ @@ -304,7 +306,7 @@ class QueryResult(proto.Message): StreamingRecognitionResult. action (str): The action name from the matched intent. - parameters (~.struct.Struct): + parameters (google.protobuf.struct_pb2.Struct): The collection of extracted parameters. Depending on your protocol or client library language, this is a map, associative array, @@ -315,9 +317,10 @@ class QueryResult(proto.Message): - 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: + composite entity: map - Else: depending on + parameter value type, could be one of string, + number, boolean, null, list or map + - MapValue value: - If parameter's entity type is a composite entity: map from composite entity property names to property values - @@ -335,23 +338,23 @@ class QueryResult(proto.Message): The text to be pronounced to the user or shown on the screen. Note: This is a legacy field, ``fulfillment_messages`` should be preferred. - fulfillment_messages (Sequence[~.gcd_intent.Intent.Message]): + fulfillment_messages (Sequence[google.cloud.dialogflow_v2.types.Intent.Message]): The collection of rich messages to present to the user. webhook_source (str): If the query was fulfilled by a webhook call, this field is set to the value of the ``source`` field returned in the webhook response. - webhook_payload (~.struct.Struct): + webhook_payload (google.protobuf.struct_pb2.Struct): If the query was fulfilled by a webhook call, this field is set to the value of the ``payload`` field returned in the webhook response. - output_contexts (Sequence[~.context.Context]): + output_contexts (Sequence[google.cloud.dialogflow_v2.types.Context]): The collection of output contexts. If applicable, ``output_contexts.parameters`` contains entries with name ``.original`` containing the original parameter values before the query. - intent (~.gcd_intent.Intent): + intent (google.cloud.dialogflow_v2.types.Intent): The intent that matched the conversational query. Some, not all fields are filled in this message, including but not limited to: ``name``, ``display_name``, ``end_interaction`` @@ -367,7 +370,7 @@ class QueryResult(proto.Message): ``multiple knowledge_answers`` messages, this value is set to the greatest ``knowledgeAnswers.match_confidence`` value in the list. - diagnostic_info (~.struct.Struct): + diagnostic_info (google.protobuf.struct_pb2.Struct): Free-form diagnostic information for the associated detect intent request. The fields of this data can change without notice, so you @@ -377,7 +380,7 @@ class QueryResult(proto.Message): - webhook call latency - webhook errors - sentiment_analysis_result (~.gcd_session.SentimentAnalysisResult): + sentiment_analysis_result (google.cloud.dialogflow_v2.types.SentimentAnalysisResult): The sentiment analysis result, which depends on the ``sentiment_analysis_request_config`` specified in the request. @@ -479,9 +482,9 @@ class StreamingDetectIntentRequest(proto.Message): Note: Always use agent versions for production traffic. See `Versions and environments `__. - query_params (~.gcd_session.QueryParameters): + query_params (google.cloud.dialogflow_v2.types.QueryParameters): The parameters of this query. - query_input (~.gcd_session.QueryInput): + query_input (google.cloud.dialogflow_v2.types.QueryInput): Required. The input specification. It can be set to: 1. an audio config which instructs the speech @@ -502,12 +505,12 @@ class StreamingDetectIntentRequest(proto.Message): is received, the client should close the stream and start a new request with a new stream as needed. This setting is ignored when ``query_input`` is a piece of text or an event. - output_audio_config (~.gcd_audio_config.OutputAudioConfig): + output_audio_config (google.cloud.dialogflow_v2.types.OutputAudioConfig): Instructs the speech synthesizer how to generate the output audio. If this field is not set and agent-level speech synthesizer is not configured, no output audio is generated. - output_audio_config_mask (~.field_mask.FieldMask): + output_audio_config_mask (google.protobuf.field_mask_pb2.FieldMask): Mask for [output_audio_config][google.cloud.dialogflow.v2.StreamingDetectIntentRequest.output_audio_config] indicating which settings in this request-level config @@ -563,12 +566,12 @@ class StreamingDetectIntentResponse(proto.Message): The unique identifier of the response. It can be used to locate a response in the training example set or for reporting issues. - recognition_result (~.gcd_session.StreamingRecognitionResult): + recognition_result (google.cloud.dialogflow_v2.types.StreamingRecognitionResult): The result of speech recognition. - query_result (~.gcd_session.QueryResult): + query_result (google.cloud.dialogflow_v2.types.QueryResult): The result of the conversational query or event processing. - webhook_status (~.status.Status): + webhook_status (google.rpc.status_pb2.Status): Specifies the status of the webhook request. output_audio (bytes): The audio data bytes encoded as specified in the request. @@ -582,7 +585,7 @@ class StreamingDetectIntentResponse(proto.Message): 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 (~.gcd_audio_config.OutputAudioConfig): + output_audio_config (google.cloud.dialogflow_v2.types.OutputAudioConfig): The config used by the speech synthesizer to generate the output audio. """ @@ -638,7 +641,7 @@ class StreamingRecognitionResult(proto.Message): - for ``END_OF_SINGLE_UTTERANCE``: only ``message_type``. Attributes: - message_type (~.gcd_session.StreamingRecognitionResult.MessageType): + message_type (google.cloud.dialogflow_v2.types.StreamingRecognitionResult.MessageType): Type of the result message. transcript (str): Transcript text representing the words that the user spoke. @@ -659,13 +662,13 @@ class StreamingRecognitionResult(proto.Message): This field is typically only provided if ``is_final`` is true and you should not rely on it being accurate or even set. - speech_word_info (Sequence[~.gcd_audio_config.SpeechWordInfo]): + speech_word_info (Sequence[google.cloud.dialogflow_v2.types.SpeechWordInfo]): Word-specific information for the words recognized by Speech in [transcript][google.cloud.dialogflow.v2.StreamingRecognitionResult.transcript]. Populated if and only if ``message_type`` = ``TRANSCRIPT`` and [InputAudioConfig.enable_word_info] is set. - speech_end_offset (~.duration.Duration): + speech_end_offset (google.protobuf.duration_pb2.Duration): Time offset of the end of this Speech recognition result relative to the beginning of the audio. Only populated for ``message_type`` = ``TRANSCRIPT``. @@ -725,7 +728,7 @@ class EventInput(proto.Message): Attributes: name (str): Required. The unique identifier of the event. - parameters (~.struct.Struct): + parameters (google.protobuf.struct_pb2.Struct): The collection of parameters associated with the event. Depending on your protocol or client library @@ -737,9 +740,10 @@ class EventInput(proto.Message): - 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: + composite entity: map - Else: depending on + parameter value type, could be one of string, + number, boolean, null, list or map + - MapValue value: - If parameter's entity type is a composite entity: map from composite entity property names to property values - @@ -789,7 +793,7 @@ class SentimentAnalysisResult(proto.Message): [ConversationProfile.human_agent_assistant_config][google.cloud.dialogflow.v2.ConversationProfile.human_agent_assistant_config] Attributes: - query_text_sentiment (~.gcd_session.Sentiment): + query_text_sentiment (google.cloud.dialogflow_v2.types.Sentiment): The sentiment analysis result for ``query_text``. """ diff --git a/google/cloud/dialogflow_v2/types/session_entity_type.py b/google/cloud/dialogflow_v2/types/session_entity_type.py index 37ad8d790..978eb6056 100644 --- a/google/cloud/dialogflow_v2/types/session_entity_type.py +++ b/google/cloud/dialogflow_v2/types/session_entity_type.py @@ -61,11 +61,11 @@ class SessionEntityType(proto.Message): ```` must be the display name of an existing entity type in the same agent that will be overridden or supplemented. - entity_override_mode (~.gcd_session_entity_type.SessionEntityType.EntityOverrideMode): + entity_override_mode (google.cloud.dialogflow_v2.types.SessionEntityType.EntityOverrideMode): Required. Indicates whether the additional data should override or supplement the custom entity type definition. - entities (Sequence[~.entity_type.EntityType.Entity]): + entities (Sequence[google.cloud.dialogflow_v2.types.EntityType.Entity]): Required. The collection of entities associated with this session entity type. """ @@ -119,7 +119,7 @@ class ListSessionEntityTypesResponse(proto.Message): [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.v2.SessionEntityTypes.ListSessionEntityTypes]. Attributes: - session_entity_types (Sequence[~.gcd_session_entity_type.SessionEntityType]): + session_entity_types (Sequence[google.cloud.dialogflow_v2.types.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. @@ -171,7 +171,7 @@ class CreateSessionEntityTypeRequest(proto.Message): If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. - session_entity_type (~.gcd_session_entity_type.SessionEntityType): + session_entity_type (google.cloud.dialogflow_v2.types.SessionEntityType): Required. The session entity type to create. """ @@ -187,9 +187,9 @@ class UpdateSessionEntityTypeRequest(proto.Message): [SessionEntityTypes.UpdateSessionEntityType][google.cloud.dialogflow.v2.SessionEntityTypes.UpdateSessionEntityType]. Attributes: - session_entity_type (~.gcd_session_entity_type.SessionEntityType): + session_entity_type (google.cloud.dialogflow_v2.types.SessionEntityType): Required. The session entity type to update. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ diff --git a/google/cloud/dialogflow_v2/types/validation_result.py b/google/cloud/dialogflow_v2/types/validation_result.py index 2681cf8de..a1d181327 100644 --- a/google/cloud/dialogflow_v2/types/validation_result.py +++ b/google/cloud/dialogflow_v2/types/validation_result.py @@ -28,7 +28,7 @@ class ValidationError(proto.Message): r"""Represents a single validation error. Attributes: - severity (~.validation_result.ValidationError.Severity): + severity (google.cloud.dialogflow_v2.types.ValidationError.Severity): The severity of the error. entries (Sequence[str]): The names of the entries that the error is @@ -73,7 +73,7 @@ class ValidationResult(proto.Message): r"""Represents the output of agent validation. Attributes: - validation_errors (Sequence[~.validation_result.ValidationError]): + validation_errors (Sequence[google.cloud.dialogflow_v2.types.ValidationError]): Contains all validation errors. """ diff --git a/google/cloud/dialogflow_v2/types/webhook.py b/google/cloud/dialogflow_v2/types/webhook.py index df744273f..e7776734c 100644 --- a/google/cloud/dialogflow_v2/types/webhook.py +++ b/google/cloud/dialogflow_v2/types/webhook.py @@ -44,11 +44,11 @@ class WebhookRequest(proto.Message): response_id (str): The unique identifier of the response. Contains the same value as ``[Streaming]DetectIntentResponse.response_id``. - query_result (~.gcd_session.QueryResult): + query_result (google.cloud.dialogflow_v2.types.QueryResult): The result of the conversational query or event processing. Contains the same value as ``[Streaming]DetectIntentResponse.query_result``. - original_detect_intent_request (~.webhook.OriginalDetectIntentRequest): + original_detect_intent_request (google.cloud.dialogflow_v2.types.OriginalDetectIntentRequest): Optional. The contents of the original request that was passed to ``[Streaming]DetectIntent`` call. """ @@ -90,7 +90,7 @@ class WebhookResponse(proto.Message): provided, Dialogflow uses this field to populate [QueryResult.fulfillment_text][google.cloud.dialogflow.v2.QueryResult.fulfillment_text] sent to the integration or API caller. - fulfillment_messages (Sequence[~.intent.Intent.Message]): + fulfillment_messages (Sequence[google.cloud.dialogflow_v2.types.Intent.Message]): Optional. The rich response messages intended for the end-user. When provided, Dialogflow uses this field to populate @@ -102,7 +102,7 @@ class WebhookResponse(proto.Message): Dialogflow uses this field to populate [QueryResult.webhook_source][google.cloud.dialogflow.v2.QueryResult.webhook_source] sent to the integration or API caller. - payload (~.struct.Struct): + payload (google.protobuf.struct_pb2.Struct): Optional. This field can be used to pass custom data from your webhook to the integration or API caller. Arbitrary JSON objects are supported. When provided, Dialogflow uses @@ -114,18 +114,18 @@ class WebhookResponse(proto.Message): for rich response messages. See the format definition at `Google Assistant Dialogflow webhook format `__ - output_contexts (Sequence[~.context.Context]): + output_contexts (Sequence[google.cloud.dialogflow_v2.types.Context]): Optional. The collection of output contexts that will overwrite currently active contexts for the session and reset their lifespans. When provided, Dialogflow uses this field to populate [QueryResult.output_contexts][google.cloud.dialogflow.v2.QueryResult.output_contexts] sent to the integration or API caller. - followup_event_input (~.gcd_session.EventInput): + followup_event_input (google.cloud.dialogflow_v2.types.EventInput): Optional. Invokes the supplied events. When this field is set, Dialogflow ignores the ``fulfillment_text``, ``fulfillment_messages``, and ``payload`` fields. - session_entity_types (Sequence[~.session_entity_type.SessionEntityType]): + session_entity_types (Sequence[google.cloud.dialogflow_v2.types.SessionEntityType]): Optional. Additional session entity types to replace or extend developer entity types with. The entity synonyms apply to all languages and persist for the session. Setting @@ -170,7 +170,7 @@ class OriginalDetectIntentRequest(proto.Message): version (str): Optional. The version of the protocol used for this request. This field is AoG-specific. - payload (~.struct.Struct): + payload (google.protobuf.struct_pb2.Struct): Optional. This field is set to the value of the ``QueryParameters.payload`` field passed in the request. Some integrations that query a Dialogflow agent may provide diff --git a/google/cloud/dialogflow_v2beta1/__init__.py b/google/cloud/dialogflow_v2beta1/__init__.py index c407dbba4..3c8f83e65 100644 --- a/google/cloud/dialogflow_v2beta1/__init__.py +++ b/google/cloud/dialogflow_v2beta1/__init__.py @@ -16,12 +16,16 @@ # from .services.agents import AgentsClient +from .services.answer_records import AnswerRecordsClient from .services.contexts import ContextsClient +from .services.conversation_profiles import ConversationProfilesClient +from .services.conversations import ConversationsClient from .services.documents import DocumentsClient from .services.entity_types import EntityTypesClient from .services.environments import EnvironmentsClient from .services.intents import IntentsClient from .services.knowledge_bases import KnowledgeBasesClient +from .services.participants import ParticipantsClient from .services.session_entity_types import SessionEntityTypesClient from .services.sessions import SessionsClient from .types.agent import Agent @@ -37,12 +41,21 @@ from .types.agent import SetAgentRequest from .types.agent import SubAgent from .types.agent import TrainAgentRequest +from .types.answer_record import AgentAssistantFeedback +from .types.answer_record import AgentAssistantRecord +from .types.answer_record import AnswerFeedback +from .types.answer_record import AnswerRecord +from .types.answer_record import GetAnswerRecordRequest +from .types.answer_record import ListAnswerRecordsRequest +from .types.answer_record import ListAnswerRecordsResponse +from .types.answer_record import UpdateAnswerRecordRequest 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 SpeechContext from .types.audio_config import SpeechModelVariant +from .types.audio_config import SpeechToTextConfig from .types.audio_config import SpeechWordInfo from .types.audio_config import SsmlVoiceGender from .types.audio_config import SynthesizeSpeechConfig @@ -57,10 +70,43 @@ from .types.context import ListContextsRequest from .types.context import ListContextsResponse from .types.context import UpdateContextRequest +from .types.conversation import BatchCreateMessagesRequest +from .types.conversation import BatchCreateMessagesResponse +from .types.conversation import CallMatcher +from .types.conversation import CompleteConversationRequest +from .types.conversation import Conversation +from .types.conversation import ConversationPhoneNumber +from .types.conversation import CreateCallMatcherRequest +from .types.conversation import CreateConversationRequest +from .types.conversation import CreateMessageRequest +from .types.conversation import DeleteCallMatcherRequest +from .types.conversation import GetConversationRequest +from .types.conversation import ListCallMatchersRequest +from .types.conversation import ListCallMatchersResponse +from .types.conversation import ListConversationsRequest +from .types.conversation import ListConversationsResponse +from .types.conversation import ListMessagesRequest +from .types.conversation import ListMessagesResponse +from .types.conversation_event import ConversationEvent +from .types.conversation_profile import AutomatedAgentConfig +from .types.conversation_profile import ConversationProfile +from .types.conversation_profile import CreateConversationProfileRequest +from .types.conversation_profile import DeleteConversationProfileRequest +from .types.conversation_profile import GetConversationProfileRequest +from .types.conversation_profile import HumanAgentAssistantConfig +from .types.conversation_profile import HumanAgentHandoffConfig +from .types.conversation_profile import ListConversationProfilesRequest +from .types.conversation_profile import ListConversationProfilesResponse +from .types.conversation_profile import LoggingConfig +from .types.conversation_profile import NotificationConfig +from .types.conversation_profile import UpdateConversationProfileRequest from .types.document import CreateDocumentRequest from .types.document import DeleteDocumentRequest from .types.document import Document from .types.document import GetDocumentRequest +from .types.document import ImportDocumentTemplate +from .types.document import ImportDocumentsRequest +from .types.document import ImportDocumentsResponse from .types.document import KnowledgeOperationMetadata from .types.document import ListDocumentsRequest from .types.document import ListDocumentsResponse @@ -84,6 +130,8 @@ from .types.environment import ListEnvironmentsRequest from .types.environment import ListEnvironmentsResponse from .types.gcs import GcsSource +from .types.gcs import GcsSources +from .types.human_agent_assistant_event import HumanAgentAssistantEvent from .types.intent import BatchDeleteIntentsRequest from .types.intent import BatchUpdateIntentsRequest from .types.intent import BatchUpdateIntentsResponse @@ -103,6 +151,43 @@ from .types.knowledge_base import ListKnowledgeBasesRequest from .types.knowledge_base import ListKnowledgeBasesResponse from .types.knowledge_base import UpdateKnowledgeBaseRequest +from .types.participant import AnalyzeContentRequest +from .types.participant import AnalyzeContentResponse +from .types.participant import AnnotatedMessagePart +from .types.participant import ArticleAnswer +from .types.participant import AudioInput +from .types.participant import AutomatedAgentReply +from .types.participant import CompileSuggestionRequest +from .types.participant import CompileSuggestionResponse +from .types.participant import CreateParticipantRequest +from .types.participant import DtmfParameters +from .types.participant import FaqAnswer +from .types.participant import GetParticipantRequest +from .types.participant import InputAudio +from .types.participant import InputText +from .types.participant import InputTextConfig +from .types.participant import ListParticipantsRequest +from .types.participant import ListParticipantsResponse +from .types.participant import ListSuggestionsRequest +from .types.participant import ListSuggestionsResponse +from .types.participant import Message +from .types.participant import MessageAnnotation +from .types.participant import OutputAudio +from .types.participant import Participant +from .types.participant import ResponseMessage +from .types.participant import SmartReplyAnswer +from .types.participant import StreamingAnalyzeContentRequest +from .types.participant import StreamingAnalyzeContentResponse +from .types.participant import SuggestArticlesRequest +from .types.participant import SuggestArticlesResponse +from .types.participant import SuggestFaqAnswersRequest +from .types.participant import SuggestFaqAnswersResponse +from .types.participant import SuggestSmartRepliesRequest +from .types.participant import SuggestSmartRepliesResponse +from .types.participant import Suggestion +from .types.participant import SuggestionFeature +from .types.participant import SuggestionResult +from .types.participant import UpdateParticipantRequest from .types.session import DetectIntentRequest from .types.session import DetectIntentResponse from .types.session import EventInput @@ -133,9 +218,23 @@ __all__ = ( "Agent", + "AgentAssistantFeedback", + "AgentAssistantRecord", "AgentsClient", + "AnalyzeContentRequest", + "AnalyzeContentResponse", + "AnnotatedMessagePart", + "AnswerFeedback", + "AnswerRecord", + "AnswerRecordsClient", + "ArticleAnswer", "AudioEncoding", + "AudioInput", + "AutomatedAgentConfig", + "AutomatedAgentReply", "BatchCreateEntitiesRequest", + "BatchCreateMessagesRequest", + "BatchCreateMessagesResponse", "BatchDeleteEntitiesRequest", "BatchDeleteEntityTypesRequest", "BatchDeleteIntentsRequest", @@ -144,17 +243,34 @@ "BatchUpdateEntityTypesResponse", "BatchUpdateIntentsRequest", "BatchUpdateIntentsResponse", + "CallMatcher", + "CompileSuggestionRequest", + "CompileSuggestionResponse", + "CompleteConversationRequest", "Context", "ContextsClient", + "Conversation", + "ConversationEvent", + "ConversationPhoneNumber", + "ConversationProfile", + "ConversationProfilesClient", + "ConversationsClient", + "CreateCallMatcherRequest", "CreateContextRequest", + "CreateConversationProfileRequest", + "CreateConversationRequest", "CreateDocumentRequest", "CreateEntityTypeRequest", "CreateIntentRequest", "CreateKnowledgeBaseRequest", + "CreateMessageRequest", + "CreateParticipantRequest", "CreateSessionEntityTypeRequest", "DeleteAgentRequest", "DeleteAllContextsRequest", + "DeleteCallMatcherRequest", "DeleteContextRequest", + "DeleteConversationProfileRequest", "DeleteDocumentRequest", "DeleteEntityTypeRequest", "DeleteIntentRequest", @@ -163,6 +279,8 @@ "DetectIntentRequest", "DetectIntentResponse", "Document", + "DocumentsClient", + "DtmfParameters", "EntityType", "EntityTypeBatch", "EntityTypesClient", @@ -171,27 +289,49 @@ "EventInput", "ExportAgentRequest", "ExportAgentResponse", + "FaqAnswer", "GcsSource", + "GcsSources", "GetAgentRequest", + "GetAnswerRecordRequest", "GetContextRequest", + "GetConversationProfileRequest", + "GetConversationRequest", "GetDocumentRequest", "GetEntityTypeRequest", "GetIntentRequest", "GetKnowledgeBaseRequest", + "GetParticipantRequest", "GetSessionEntityTypeRequest", "GetValidationResultRequest", + "HumanAgentAssistantConfig", + "HumanAgentAssistantEvent", + "HumanAgentHandoffConfig", "ImportAgentRequest", + "ImportDocumentTemplate", + "ImportDocumentsRequest", + "ImportDocumentsResponse", + "InputAudio", "InputAudioConfig", + "InputText", + "InputTextConfig", "Intent", "IntentBatch", "IntentView", "IntentsClient", "KnowledgeAnswers", "KnowledgeBase", - "KnowledgeBasesClient", "KnowledgeOperationMetadata", + "ListAnswerRecordsRequest", + "ListAnswerRecordsResponse", + "ListCallMatchersRequest", + "ListCallMatchersResponse", "ListContextsRequest", "ListContextsResponse", + "ListConversationProfilesRequest", + "ListConversationProfilesResponse", + "ListConversationsRequest", + "ListConversationsResponse", "ListDocumentsRequest", "ListDocumentsResponse", "ListEntityTypesRequest", @@ -202,15 +342,29 @@ "ListIntentsResponse", "ListKnowledgeBasesRequest", "ListKnowledgeBasesResponse", + "ListMessagesRequest", + "ListMessagesResponse", + "ListParticipantsRequest", + "ListParticipantsResponse", "ListSessionEntityTypesRequest", "ListSessionEntityTypesResponse", + "ListSuggestionsRequest", + "ListSuggestionsResponse", + "LoggingConfig", + "Message", + "MessageAnnotation", + "NotificationConfig", "OriginalDetectIntentRequest", + "OutputAudio", "OutputAudioConfig", "OutputAudioEncoding", + "Participant", + "ParticipantsClient", "QueryInput", "QueryParameters", "QueryResult", "ReloadDocumentRequest", + "ResponseMessage", "RestoreAgentRequest", "SearchAgentsRequest", "SearchAgentsResponse", @@ -221,29 +375,45 @@ "SessionEntityTypesClient", "SessionsClient", "SetAgentRequest", + "SmartReplyAnswer", "SpeechContext", "SpeechModelVariant", + "SpeechToTextConfig", "SpeechWordInfo", "SsmlVoiceGender", + "StreamingAnalyzeContentRequest", + "StreamingAnalyzeContentResponse", "StreamingDetectIntentRequest", "StreamingDetectIntentResponse", "StreamingRecognitionResult", "SubAgent", + "SuggestArticlesRequest", + "SuggestArticlesResponse", + "SuggestFaqAnswersRequest", + "SuggestFaqAnswersResponse", + "SuggestSmartRepliesRequest", + "SuggestSmartRepliesResponse", + "Suggestion", + "SuggestionFeature", + "SuggestionResult", "SynthesizeSpeechConfig", "TelephonyDtmf", "TelephonyDtmfEvents", "TextInput", "TrainAgentRequest", + "UpdateAnswerRecordRequest", "UpdateContextRequest", + "UpdateConversationProfileRequest", "UpdateDocumentRequest", "UpdateEntityTypeRequest", "UpdateIntentRequest", "UpdateKnowledgeBaseRequest", + "UpdateParticipantRequest", "UpdateSessionEntityTypeRequest", "ValidationError", "ValidationResult", "VoiceSelectionParams", "WebhookRequest", "WebhookResponse", - "DocumentsClient", + "KnowledgeBasesClient", ) diff --git a/google/cloud/dialogflow_v2beta1/proto/agent.proto b/google/cloud/dialogflow_v2beta1/proto/agent.proto index bb413e845..51c5b831d 100644 --- a/google/cloud/dialogflow_v2beta1/proto/agent.proto +++ b/google/cloud/dialogflow_v2beta1/proto/agent.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/google/cloud/dialogflow_v2beta1/proto/answer_record.proto b/google/cloud/dialogflow_v2beta1/proto/answer_record.proto new file mode 100644 index 000000000..b344010d1 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/proto/answer_record.proto @@ -0,0 +1,322 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2beta1; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/cloud/dialogflow/v2beta1/participant.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2beta1"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2beta1;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "AnswerRecordsProto"; +option java_package = "com.google.cloud.dialogflow.v2beta1"; +option objc_class_prefix = "DF"; + +// Service for managing [AnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecord]. +service AnswerRecords { + option (google.api.default_host) = "dialogflow.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/dialogflow"; + + // Deprecated. + // Retrieves a specific answer record. + rpc GetAnswerRecord(GetAnswerRecordRequest) returns (AnswerRecord) { + option deprecated = true; + option (google.api.http) = { + get: "/v2beta1/{name=projects/*/answerRecords/*}" + additional_bindings { + get: "/v2beta1/{name=projects/*/locations/*/answerRecords/*}" + } + }; + } + + // Returns the list of all answer records in the specified project in reverse + // chronological order. + rpc ListAnswerRecords(ListAnswerRecordsRequest) returns (ListAnswerRecordsResponse) { + option (google.api.http) = { + get: "/v2beta1/{parent=projects/*}/answerRecords" + additional_bindings { + get: "/v2beta1/{parent=projects/*/locations/*}/answerRecords" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Updates the specified answer record. + rpc UpdateAnswerRecord(UpdateAnswerRecordRequest) returns (AnswerRecord) { + option (google.api.http) = { + patch: "/v2beta1/{answer_record.name=projects/*/answerRecords/*}" + body: "answer_record" + additional_bindings { + patch: "/v2beta1/{answer_record.name=projects/*/locations/*/answerRecords/*}" + body: "answer_record" + } + }; + option (google.api.method_signature) = "answer_record,update_mask"; + } +} + +// Answer records are records to manage answer history and feedbacks for +// Dialogflow. +// +// Currently, answer record includes: +// +// - human agent assistant article suggestion +// - human agent assistant faq article +// +// It doesn't include: +// +// - `DetectIntent` intent matching +// - `DetectIntent` knowledge +// +// Answer records are not related to the conversation history in the +// Dialogflow Console. A Record is generated even when the end-user disables +// conversation history in the console. Records are created when there's a human +// agent assistant suggestion generated. +// +// A typical workflow for customers provide feedback to an answer is: +// +// 1. For human agent assistant, customers get suggestion via ListSuggestions +// API. Together with the answers, [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] are returned to the +// customers. +// 2. The customer uses the [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] to call the +// [UpdateAnswerRecord][] method to send feedback about a specific answer +// that they believe is wrong. +message AnswerRecord { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/AnswerRecord" + pattern: "projects/{project}/answerRecords/{answer_record}" + pattern: "projects/{project}/locations/{location}/answerRecords/{answer_record}" + }; + + // The unique identifier of this answer record. + // Required for [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.UpdateAnswerRecord] method. + // Format: `projects//locations//answerRecords/`. + string name = 1; + + // Optional. The AnswerFeedback for this record. You can set this with + // [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.UpdateAnswerRecord] in order to give us feedback about + // this answer. + AnswerFeedback answer_feedback = 3; + + // Output only. The record for this answer. + oneof record { + // Output only. The record for human agent assistant. + AgentAssistantRecord agent_assistant_record = 4; + } +} + +// Represents a record of a human agent assistant answer. +message AgentAssistantRecord { + // Output only. The agent assistant answer. + oneof answer { + // Output only. The article suggestion answer. + ArticleAnswer article_suggestion_answer = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The FAQ answer. + FaqAnswer faq_answer = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; + } +} + +// Represents feedback the customer has about the quality & correctness of a +// certain answer in a conversation. +message AnswerFeedback { + // The correctness level of an answer. + enum CorrectnessLevel { + // Correctness level unspecified. + CORRECTNESS_LEVEL_UNSPECIFIED = 0; + + // Answer is totally wrong. + NOT_CORRECT = 1; + + // Answer is partially correct. + PARTIALLY_CORRECT = 2; + + // Answer is fully correct. + FULLY_CORRECT = 3; + } + + // The correctness level of the specific answer. + CorrectnessLevel correctness_level = 1; + + // Normally, detail feedback is provided when answer is not fully correct. + oneof detail_feedback { + // Optional. Detail feedback of agent assistant suggestions. + AgentAssistantFeedback agent_assistant_detail_feedback = 2; + } + + // Indicates whether the answer/item was clicked by the human agent + // or not. Default to false. + bool clicked = 3; + + // Time when the answer/item was clicked. + google.protobuf.Timestamp click_time = 5; + + // Indicates whether the answer/item was displayed to the human + // agent in the agent desktop UI. Default to false. + bool displayed = 4; + + // Time when the answer/item was displayed. + google.protobuf.Timestamp display_time = 6; +} + +// Detail feedback of Agent Assistant result. +message AgentAssistantFeedback { + // Feedback for conversation summarization. + message SummarizationFeedback { + // Timestamp when composing of the summary starts. + google.protobuf.Timestamp start_timestamp = 1; + + // Timestamp when the summary was submitted. + google.protobuf.Timestamp submit_timestamp = 2; + + // Text of actual submitted summary. + string summary_text = 3; + } + + // Relevance of an answer. + enum AnswerRelevance { + // Answer relevance unspecified. + ANSWER_RELEVANCE_UNSPECIFIED = 0; + + // Answer is irrelevant to query. + IRRELEVANT = 1; + + // Answer is relevant to query. + RELEVANT = 2; + } + + // Correctness of document. + enum DocumentCorrectness { + // Document correctness unspecified. + DOCUMENT_CORRECTNESS_UNSPECIFIED = 0; + + // Information in document is incorrect. + INCORRECT = 1; + + // Information in document is correct. + CORRECT = 2; + } + + // Efficiency of document. + enum DocumentEfficiency { + // Document efficiency unspecified. + DOCUMENT_EFFICIENCY_UNSPECIFIED = 0; + + // Document is inefficient. + INEFFICIENT = 1; + + // Document is efficient. + EFFICIENT = 2; + } + + // Optional. Whether or not the suggested answer is relevant. + // + // For example: + // + // * Query: "Can I change my mailing address?" + // * Suggested document says: "Items must be returned/exchanged within 60 + // days of the purchase date." + // * [answer_relevance][google.cloud.dialogflow.v2beta1.AgentAssistantFeedback.answer_relevance]: [AnswerRelevance.IRRELEVANT][google.cloud.dialogflow.v2beta1.AgentAssistantFeedback.AnswerRelevance.IRRELEVANT] + AnswerRelevance answer_relevance = 1; + + // Optional. Whether or not the information in the document is correct. + // + // For example: + // + // * Query: "Can I return the package in 2 days once received?" + // * Suggested document says: "Items must be returned/exchanged within 60 + // days of the purchase date." + // * Ground truth: "No return or exchange is allowed." + // * [document_correctness]: INCORRECT + DocumentCorrectness document_correctness = 2; + + // Optional. Whether or not the suggested document is efficient. For example, + // if the document is poorly written, hard to understand, hard to use or + // too long to find useful information, [document_efficiency][google.cloud.dialogflow.v2beta1.AgentAssistantFeedback.document_efficiency] is + // [DocumentEfficiency.INEFFICIENT][google.cloud.dialogflow.v2beta1.AgentAssistantFeedback.DocumentEfficiency.INEFFICIENT]. + DocumentEfficiency document_efficiency = 3; + + // Feedback for conversation summarization. + SummarizationFeedback summarization_feedback = 4; +} + +// Request message for [AnswerRecords.GetAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.GetAnswerRecord]. +message GetAnswerRecordRequest { + // Required. The name of the answer record to retrieve. + // Format: `projects//locations//answerRecords/`. + string name = 1; +} + +// Request message for [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecords.ListAnswerRecords]. +message ListAnswerRecordsRequest { + // Required. The project to list all answer records for in reverse + // chronological order. Format: `projects//locations/`. + string parent = 1 [(google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/AnswerRecord" + }]; + + // Optional. The maximum number of records to return in a single page. + // The server may return fewer records than this. If unspecified, we use 10. + // The maximum is 100. + int32 page_size = 3; + + // Optional. The + // [ListAnswerRecordsResponse.next_page_token][google.cloud.dialogflow.v2beta1.ListAnswerRecordsResponse.next_page_token] + // value returned from a previous list request used to continue listing on + // the next page. + string page_token = 4; +} + +// Response message for [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecords.ListAnswerRecords]. +message ListAnswerRecordsResponse { + // The list of answer records. + repeated AnswerRecord answer_records = 1; + + // A token to retrieve next page of results. Or empty if there are no more + // results. + // Pass this value in the + // [ListAnswerRecordsRequest.page_token][google.cloud.dialogflow.v2beta1.ListAnswerRecordsRequest.page_token] + // field in the subsequent call to `ListAnswerRecords` method to retrieve the + // next page of results. + string next_page_token = 2; +} + +// Request message for [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.UpdateAnswerRecord]. +message UpdateAnswerRecordRequest { + // Required. Answer record to update. + AnswerRecord answer_record = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/AnswerRecord" + } + ]; + + // Required. The mask to control which fields get updated. + google.protobuf.FieldMask update_mask = 2; +} diff --git a/google/cloud/dialogflow_v2beta1/proto/audio_config.proto b/google/cloud/dialogflow_v2beta1/proto/audio_config.proto index f9a9d53e7..009a9473b 100644 --- a/google/cloud/dialogflow_v2beta1/proto/audio_config.proto +++ b/google/cloud/dialogflow_v2beta1/proto/audio_config.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -248,6 +248,12 @@ message InputAudioConfig { // Note: When specified, InputAudioConfig.single_utterance takes precedence // over StreamingDetectIntentRequest.single_utterance. bool single_utterance = 8; + + // Only used in [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent] and + // [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.StreamingAnalyzeContent]. + // If `false` and recognition doesn't return any result, trigger + // `NO_SPEECH_RECOGNIZED` event to Dialogflow agent. + bool disable_no_speech_recognized_event = 14; } // Description of which voice to use for speech synthesis. @@ -255,6 +261,9 @@ message VoiceSelectionParams { // 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.v2beta1.VoiceSelectionParams.ssml_gender]. + // + // For the list of available voices, please refer to [Supported voices and + // languages](https://cloud.google.com/text-to-speech/docs/voices). string name = 1; // Optional. The preferred gender of the voice. If not set, the service will @@ -265,23 +274,6 @@ message VoiceSelectionParams { SsmlVoiceGender ssml_gender = 2; } -// Gender of the voice as described in -// [SSML voice element](https://www.w3.org/TR/speech-synthesis11/#edef_voice). -enum SsmlVoiceGender { - // An unspecified gender, which means that the client doesn't care which - // gender the selected voice will have. - SSML_VOICE_GENDER_UNSPECIFIED = 0; - - // A male voice. - SSML_VOICE_GENDER_MALE = 1; - - // A female voice. - SSML_VOICE_GENDER_FEMALE = 2; - - // A gender-neutral voice. - SSML_VOICE_GENDER_NEUTRAL = 3; -} - // Configuration of how speech should be synthesized. message SynthesizeSpeechConfig { // Optional. Speaking rate/speed, in the range [0.25, 4.0]. 1.0 is the normal @@ -314,6 +306,23 @@ message SynthesizeSpeechConfig { VoiceSelectionParams voice = 4; } +// Gender of the voice as described in +// [SSML voice element](https://www.w3.org/TR/speech-synthesis11/#edef_voice). +enum SsmlVoiceGender { + // An unspecified gender, which means that the client doesn't care which + // gender the selected voice will have. + SSML_VOICE_GENDER_UNSPECIFIED = 0; + + // A male voice. + SSML_VOICE_GENDER_MALE = 1; + + // A female voice. + SSML_VOICE_GENDER_FEMALE = 2; + + // A gender-neutral voice. + SSML_VOICE_GENDER_NEUTRAL = 3; +} + // Instructs the speech synthesizer how to generate the output audio content. // If this audio config is supplied in a request, it overrides all existing // text-to-speech settings applied to the agent. @@ -332,6 +341,12 @@ message OutputAudioConfig { SynthesizeSpeechConfig synthesize_speech_config = 3; } +// A wrapper of repeated TelephonyDtmf digits. +message TelephonyDtmfEvents { + // A sequence of TelephonyDtmf digits. + repeated TelephonyDtmf dtmf_events = 1; +} + // Audio encoding of the output audio format in Text-To-Speech. enum OutputAudioEncoding { // Not specified. @@ -351,10 +366,13 @@ enum OutputAudioEncoding { OUTPUT_AUDIO_ENCODING_OGG_OPUS = 3; } -// A wrapper of repeated TelephonyDtmf digits. -message TelephonyDtmfEvents { - // A sequence of TelephonyDtmf digits. - repeated TelephonyDtmf dtmf_events = 1; +// Configures speech transcription for [ConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfile]. +message SpeechToTextConfig { + // Optional. The speech model used in speech to text. + // `SPEECH_MODEL_VARIANT_UNSPECIFIED`, `USE_BEST_AVAILABLE` will be treated as + // `USE_ENHANCED`. It can be overridden in [AnalyzeContentRequest][google.cloud.dialogflow.v2beta1.AnalyzeContentRequest] and + // [StreamingAnalyzeContentRequest][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest] request. + SpeechModelVariant speech_model_variant = 1 [(google.api.field_behavior) = OPTIONAL]; } // [DTMF](https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling) diff --git a/google/cloud/dialogflow_v2beta1/proto/context.proto b/google/cloud/dialogflow_v2beta1/proto/context.proto index 949552057..a48e0ab63 100644 --- a/google/cloud/dialogflow_v2beta1/proto/context.proto +++ b/google/cloud/dialogflow_v2beta1/proto/context.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -217,7 +217,8 @@ message Context { // - 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 + // - Else: depending on parameter value type, could be one of string, + // number, boolean, null, list or map // - MapValue value: // - If parameter's entity type is a composite entity: // map from composite entity property names to property values diff --git a/google/cloud/dialogflow_v2beta1/proto/conversation.proto b/google/cloud/dialogflow_v2beta1/proto/conversation.proto new file mode 100644 index 000000000..068473823 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/proto/conversation.proto @@ -0,0 +1,581 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2beta1; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/cloud/dialogflow/v2beta1/gcs.proto"; +import "google/cloud/dialogflow/v2beta1/participant.proto"; +import "google/cloud/dialogflow/v2beta1/session.proto"; +import "google/longrunning/operations.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; +import "google/rpc/status.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2beta1"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2beta1;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "ConversationProto"; +option java_package = "com.google.cloud.dialogflow.v2beta1"; +option objc_class_prefix = "DF"; + +// Service for managing [Conversations][google.cloud.dialogflow.v2beta1.Conversation]. +service Conversations { + option (google.api.default_host) = "dialogflow.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/dialogflow"; + + // Creates a new conversation. Conversations are auto-completed after 24 + // hours. + // + // Conversation Lifecycle: + // There are two stages during a conversation: Automated Agent Stage and + // Assist Stage. + // + // For Automated Agent Stage, there will be a dialogflow agent responding to + // user queries. + // + // For Assist Stage, there's no dialogflow agent responding to user queries. + // But we will provide suggestions which are generated from conversation. + // + // If [Conversation.conversation_profile][google.cloud.dialogflow.v2beta1.Conversation.conversation_profile] is configured for a dialogflow + // agent, conversation will start from `Automated Agent Stage`, otherwise, it + // will start from `Assist Stage`. And during `Automated Agent Stage`, once an + // [Intent][google.cloud.dialogflow.v2beta1.Intent] with [Intent.live_agent_handoff][google.cloud.dialogflow.v2beta1.Intent.live_agent_handoff] is triggered, conversation + // will transfer to Assist Stage. + rpc CreateConversation(CreateConversationRequest) returns (Conversation) { + option (google.api.http) = { + post: "/v2beta1/{parent=projects/*}/conversations" + body: "conversation" + additional_bindings { + post: "/v2beta1/{parent=projects/*/locations/*}/conversations" + body: "conversation" + } + }; + option (google.api.method_signature) = "parent,conversation"; + } + + // Returns the list of all conversations in the specified project. + rpc ListConversations(ListConversationsRequest) returns (ListConversationsResponse) { + option (google.api.http) = { + get: "/v2beta1/{parent=projects/*}/conversations" + additional_bindings { + get: "/v2beta1/{parent=projects/*/locations/*}/conversations" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Retrieves the specific conversation. + rpc GetConversation(GetConversationRequest) returns (Conversation) { + option (google.api.http) = { + get: "/v2beta1/{name=projects/*/conversations/*}" + additional_bindings { + get: "/v2beta1/{name=projects/*/locations/*/conversations/*}" + } + }; + option (google.api.method_signature) = "name"; + } + + // Completes the specified conversation. Finished conversations are purged + // from the database after 30 days. + rpc CompleteConversation(CompleteConversationRequest) returns (Conversation) { + option (google.api.http) = { + post: "/v2beta1/{name=projects/*/conversations/*}:complete" + body: "*" + additional_bindings { + post: "/v2beta1/{name=projects/*/locations/*/conversations/*}:complete" + body: "*" + } + }; + option (google.api.method_signature) = "name"; + } + + // Creates a call matcher that links incoming SIP calls to the specified + // conversation if they fulfill specified criteria. + rpc CreateCallMatcher(CreateCallMatcherRequest) returns (CallMatcher) { + option (google.api.http) = { + post: "/v2beta1/{parent=projects/*/conversations/*}/callMatchers" + body: "call_matcher" + additional_bindings { + post: "/v2beta1/{parent=projects/*/locations/*/conversations/*}/callMatchers" + body: "*" + } + }; + option (google.api.method_signature) = "parent,call_matcher"; + } + + // Returns the list of all call matchers in the specified conversation. + rpc ListCallMatchers(ListCallMatchersRequest) returns (ListCallMatchersResponse) { + option (google.api.http) = { + get: "/v2beta1/{parent=projects/*/conversations/*}/callMatchers" + additional_bindings { + get: "/v2beta1/{parent=projects/*/locations/*/conversations/*}/callMatchers" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Requests deletion of a call matcher. + rpc DeleteCallMatcher(DeleteCallMatcherRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v2beta1/{name=projects/*/conversations/*/callMatchers/*}" + additional_bindings { + delete: "/v2beta1/{name=projects/*/locations/*/conversations/*/callMatchers/*}" + } + }; + option (google.api.method_signature) = "name"; + } + + // Batch ingests messages to conversation. Customers can use this RPC to + // ingest historical messages to conversation. + rpc BatchCreateMessages(BatchCreateMessagesRequest) returns (BatchCreateMessagesResponse) { + option (google.api.http) = { + post: "/v2beta1/{parent=projects/*/conversations/*}/messages:batchCreate" + body: "*" + additional_bindings { + post: "/v2beta1/{parent=projects/*/locations/*/conversations/*}/messages:batchCreate" + body: "*" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Lists messages that belong to a given conversation. + // `messages` are ordered by `create_time` in descending order. To fetch + // updates without duplication, send request with filter + // `create_time_epoch_microseconds > + // [first item's create_time of previous request]` and empty page_token. + rpc ListMessages(ListMessagesRequest) returns (ListMessagesResponse) { + option (google.api.http) = { + get: "/v2beta1/{parent=projects/*/conversations/*}/messages" + additional_bindings { + get: "/v2beta1/{parent=projects/*/locations/*/conversations/*}/messages" + } + }; + option (google.api.method_signature) = "parent"; + } +} + +// Represents a conversation. +// A conversation is an interaction between an agent, including live agents +// and Dialogflow agents, and a support customer. Conversations can +// include phone calls and text-based chat sessions. +message Conversation { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/Conversation" + pattern: "projects/{project}/conversations/{conversation}" + pattern: "projects/{project}/locations/{location}/conversations/{conversation}" + }; + + // Enumeration of the completion status of the conversation. + enum LifecycleState { + // Unknown. + LIFECYCLE_STATE_UNSPECIFIED = 0; + + // Conversation is currently open for media analysis. + IN_PROGRESS = 1; + + // Conversation has been completed. + COMPLETED = 2; + } + + // Enumeration of the different conversation stages a conversation can be in. + // Reference: + // https://cloud.google.com/dialogflow/priv/docs/contact-center/basics#stages + enum ConversationStage { + // Unknown. Should never be used after a conversation is successfully + // created. + CONVERSATION_STAGE_UNSPECIFIED = 0; + + // The conversation should return virtual agent responses into the + // conversation. + VIRTUAL_AGENT_STAGE = 1; + + // The conversation should not provide responses, just listen and provide + // suggestions. + HUMAN_ASSIST_STAGE = 2; + } + + // Output only. The unique identifier of this conversation. + // Format: `projects//locations//conversations/`. + string name = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The current state of the Conversation. + LifecycleState lifecycle_state = 2 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Required. The Conversation Profile to be used to configure this + // Conversation. This field cannot be updated. + // Format: `projects//locations//conversationProfiles/`. + string conversation_profile = 3 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/ConversationProfile" + } + ]; + + // Output only. Required if the conversation is to be connected over + // telephony. + ConversationPhoneNumber phone_number = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // The stage of a conversation. It indicates whether the virtual agent or a + // human agent is handling the conversation. + // + // If the conversation is created with the conversation profile that has + // Dialogflow config set, defaults to + // [ConversationStage.VIRTUAL_AGENT_STAGE][google.cloud.dialogflow.v2beta1.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE]; Otherwise, defaults to + // [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2beta1.Conversation.ConversationStage.HUMAN_ASSIST_STAGE]. + // + // If the conversation is created with the conversation profile that has + // Dialogflow config set but explicitly sets conversation_stage to + // [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2beta1.Conversation.ConversationStage.HUMAN_ASSIST_STAGE], it skips + // [ConversationStage.VIRTUAL_AGENT_STAGE][google.cloud.dialogflow.v2beta1.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE] stage and directly goes to + // [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2beta1.Conversation.ConversationStage.HUMAN_ASSIST_STAGE]. + ConversationStage conversation_stage = 7; + + // Output only. The time the conversation was started. + google.protobuf.Timestamp start_time = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The time the conversation was finished. + google.protobuf.Timestamp end_time = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; +} + +// Represents a phone number for telephony integration. It allows for connecting +// a particular conversation over telephony. +message ConversationPhoneNumber { + // Output only. The phone number to connect to this conversation. + string phone_number = 3 [(google.api.field_behavior) = OUTPUT_ONLY]; +} + +// Represents a call matcher that describes criteria for matching incoming SIP +// calls to a conversation. When Dialogflow get a SIP call from a third-party +// carrier, Dialogflow matches the call to an existing conversation by either: +// +// * Extracting the conversation id from the +// [Call-Info header](https://tools.ietf.org/html/rfc3261#section-20.9), e.g. +// `Call-Info: +// +// ;purpose=Goog-ContactCenter-Conversation`. +// * Or, if that doesn't work, matching incoming [SIP +// headers](https://tools.ietf.org/html/rfc3261#section-7.3) +// against any [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] for the conversation. +// +// If an incoming SIP call without valid `Call-Info` header matches to zero or +// multiple conversations with `CallMatcher`, we reject it. +// +// A call matcher contains equality conditions for SIP headers that all have +// to be fulfilled in order for a SIP call to match. +// +// The matched SIP headers consist of well-known headers (`To`, `From`, +// `Call-ID`) and custom headers. A [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] is only valid if it +// specifies: +// +// * At least 1 custom header, +// * or at least 2 well-known headers. +message CallMatcher { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/CallMatcher" + pattern: "projects/{project}/conversations/{conversation}/callMatchers/{call_matcher}" + pattern: "projects/{project}/locations/{location}/conversations/{conversation}/callMatchers/{call_matcher}" + }; + + // Custom SIP headers. See the [description of headers in + // the RFC](https://tools.ietf.org/html/rfc3261#section-7.3). + message CustomHeaders { + // Cisco's proprietary `Cisco-Guid` header. + string cisco_guid = 1; + } + + // Output only. The unique identifier of this call matcher. + // Format: `projects//locations//conversations//callMatchers/`. + string name = 1 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Value of the [`To` + // header](https://tools.ietf.org/html/rfc3261#section-8.1.1.2) to match. If + // empty or unspecified, we don't match to the + // [`To` header](https://tools.ietf.org/html/rfc3261#section-8.1.1.2). + string to_header = 2; + + // Value of the [`From` + // header](https://tools.ietf.org/html/rfc3261#section-8.1.1.3) to match. If + // empty or unspecified, we don't match to the + // [`From` header](https://tools.ietf.org/html/rfc3261#section-8.1.1.3). + string from_header = 3; + + // Value of the [`Call-ID` + // header](https://tools.ietf.org/html/rfc3261#section-8.1.1.4) to match. If + // empty or unspecified, we don't match to the + // [`Call-ID` header](https://tools.ietf.org/html/rfc3261#section-8.1.1.4). + string call_id_header = 4; + + // Custom SIP headers that must match. + CustomHeaders custom_headers = 5; +} + +// The request message for [Conversations.CreateConversation][google.cloud.dialogflow.v2beta1.Conversations.CreateConversation]. +message CreateConversationRequest { + // Required. Resource identifier of the project creating the conversation. + // Format: `projects//locations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Conversation" + } + ]; + + // Required. The conversation to create. + Conversation conversation = 2 [(google.api.field_behavior) = REQUIRED]; + + // Optional. Identifier of the conversation. Generally it's auto generated by Google. + // Only set it if you cannot wait for the response to return a + // auto-generated one to you. + // + // The conversation ID must be compliant with the regression fomula + // "[a-zA-Z][a-zA-Z0-9_-]*" with the characters length in range of [3,64]. + // If the field is provided, the caller is resposible for + // 1. the uniqueness of the ID, otherwise the request will be rejected. + // 2. the consistency for whether to use custom ID or not under a project to + // better ensure uniqueness. + string conversation_id = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// The request message for [Conversations.ListConversations][google.cloud.dialogflow.v2beta1.Conversations.ListConversations]. +message ListConversationsRequest { + // Required. The project from which to list all conversation. + // Format: `projects//locations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Conversation" + } + ]; + + // Optional. The maximum number of items to return in a single page. By + // default 100 and at most 1000. + int32 page_size = 2; + + // Optional. The next_page_token value returned from a previous list request. + string page_token = 3; + + // A filter expression that filters conversations listed in the response. In + // general, the expression must specify the field name, a comparison operator, + // and the value to use for filtering: + //
    + //
  • The value must be a string, a number, or a boolean.
  • + //
  • The comparison operator must be either `=`,`!=`, `>`, or `<`.
  • + //
  • To filter on multiple expressions, separate the + // expressions with `AND` or `OR` (omitting both implies `AND`).
  • + //
  • For clarity, expressions can be enclosed in parentheses.
  • + //
+ // Only `lifecycle_state` can be filtered on in this way. For example, + // the following expression only returns `COMPLETED` conversations: + // + // `lifecycle_state = "COMPLETED"` + // + // For more information about filtering, see + // [API Filtering](https://aip.dev/160). + string filter = 4; +} + +// The response message for [Conversations.ListConversations][google.cloud.dialogflow.v2beta1.Conversations.ListConversations]. +message ListConversationsResponse { + // The list of conversations. There will be a maximum number of items + // returned based on the page_size field in the request. + repeated Conversation conversations = 1; + + // Token to retrieve the next page of results, or empty if there are no + // more results in the list. + string next_page_token = 2; +} + +// The request message for [Conversations.GetConversation][google.cloud.dialogflow.v2beta1.Conversations.GetConversation]. +message GetConversationRequest { + // Required. The name of the conversation. Format: + // `projects//locations//conversations/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Conversation" + } + ]; +} + +// The request message for [Conversations.CompleteConversation][google.cloud.dialogflow.v2beta1.Conversations.CompleteConversation]. +message CompleteConversationRequest { + // Required. Resource identifier of the conversation to close. + // Format: `projects//locations//conversations/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Conversation" + } + ]; +} + +// The request message for [Conversations.CreateCallMatcher][google.cloud.dialogflow.v2beta1.Conversations.CreateCallMatcher]. +message CreateCallMatcherRequest { + // Required. Resource identifier of the conversation adding the call matcher. + // Format: `projects//locations//conversations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/CallMatcher" + } + ]; + + // Required. The call matcher to create. + CallMatcher call_matcher = 2; +} + +// The request message for [Conversations.ListCallMatchers][google.cloud.dialogflow.v2beta1.Conversations.ListCallMatchers]. +message ListCallMatchersRequest { + // Required. The conversation to list all call matchers from. + // Format: `projects//locations//conversations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/CallMatcher" + } + ]; + + // Optional. The maximum number of items to return in a single page. By + // default 100 and at most 1000. + int32 page_size = 2; + + // Optional. The next_page_token value returned from a previous list request. + string page_token = 3; +} + +// The response message for [Conversations.ListCallMatchers][google.cloud.dialogflow.v2beta1.Conversations.ListCallMatchers]. +message ListCallMatchersResponse { + // The list of call matchers. There is a maximum number of items + // returned based on the page_size field in the request. + repeated CallMatcher call_matchers = 1; + + // Token to retrieve the next page of results or empty if there are no + // more results in the list. + string next_page_token = 2; +} + +// The request message for [Conversations.DeleteCallMatcher][google.cloud.dialogflow.v2beta1.Conversations.DeleteCallMatcher]. +message DeleteCallMatcherRequest { + // Required. The unique identifier of the [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] to delete. + // Format: `projects//locations//conversations//callMatchers/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/CallMatcher" + } + ]; +} + +// The request message to create one Message. Currently it is only used in +// BatchCreateMessagesRequest. +message CreateMessageRequest { + // Required. Resource identifier of the conversation to create message. + // Format: `projects//locations//conversations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Conversation" + } + ]; + + // Required. The message to create. + // [Message.participant][google.cloud.dialogflow.v2beta1.Message.participant] is required. + Message message = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request message for [Conversations.BatchCreateMessagesRequest][]. +message BatchCreateMessagesRequest { + // Required. Resource identifier of the conversation to create message. + // Format: `projects//locations//conversations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Conversation" + } + ]; + + // Required. A maximum of 1000 Messages can be created in a batch. + // [CreateMessageRequest.message.send_time][] is required. All created + // messages will have identical [Message.create_time][google.cloud.dialogflow.v2beta1.Message.create_time]. + repeated CreateMessageRequest requests = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request message for [Conversations.BatchCreateMessagesResponse][]. +message BatchCreateMessagesResponse { + // Messages created. + repeated Message messages = 1; +} + +// The request message for [Conversations.ListMessages][google.cloud.dialogflow.v2beta1.Conversations.ListMessages]. +message ListMessagesRequest { + // Required. The name of the conversation to list messages for. + // Format: `projects//locations//conversations/` + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Message" + } + ]; + + // Optional. Filter on message fields. Currently predicates on `create_time` + // and `create_time_epoch_microseconds` are supported. `create_time` only + // support milliseconds accuracy. E.g., + // `create_time_epoch_microseconds > 1551790877964485` or + // `create_time > 2017-01-15T01:30:15.01Z`. + // + // For more information about filtering, see + // [API Filtering](https://aip.dev/160). + string filter = 4; + + // Optional. The maximum number of items to return in a single page. By + // default 100 and at most 1000. + int32 page_size = 2; + + // Optional. The next_page_token value returned from a previous list request. + string page_token = 3; +} + +// The response message for [Conversations.ListMessages][google.cloud.dialogflow.v2beta1.Conversations.ListMessages]. +message ListMessagesResponse { + // Required. The list of messages. There will be a maximum number of items + // returned based on the page_size field in the request. + // `messages` is sorted by `create_time` in descending order. + repeated Message messages = 1; + + // Optional. Token to retrieve the next page of results, or empty if there are + // no more results in the list. + string next_page_token = 2; +} diff --git a/google/cloud/dialogflow_v2beta1/proto/conversation_event.proto b/google/cloud/dialogflow_v2beta1/proto/conversation_event.proto new file mode 100644 index 000000000..35c1de1ee --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/proto/conversation_event.proto @@ -0,0 +1,82 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2beta1; + +import "google/cloud/dialogflow/v2beta1/participant.proto"; +import "google/rpc/status.proto"; +import "google/api/annotations.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2beta1"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2beta1;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "ConversationEventProto"; +option java_package = "com.google.cloud.dialogflow.v2beta1"; +option objc_class_prefix = "DF"; + +// Represents a notification sent to Pub/Sub subscribers for conversation +// lifecycle events. +message ConversationEvent { + // Enumeration of the types of events available. + enum Type { + // Type not set. + TYPE_UNSPECIFIED = 0; + + // A new conversation has been opened. This is fired when a telephone call + // is answered, or a conversation is created via the API. + CONVERSATION_STARTED = 1; + + // An existing conversation has closed. This is fired when a telephone call + // is terminated, or a conversation is closed via the API. + CONVERSATION_FINISHED = 2; + + // An existing conversation has received a new message, either from API or + // telephony. It is configured in + // [ConversationProfile.new_message_event_notification_config][google.cloud.dialogflow.v2beta1.ConversationProfile.new_message_event_notification_config] + NEW_MESSAGE = 5; + + // Unrecoverable error during a telephone call. + // + // In general non-recoverable errors only occur if something was + // misconfigured in the ConversationProfile corresponding to the call. After + // a non-recoverable error, Dialogflow may stop responding. + // + // We don't fire this event: + // + // * in an API call because we can directly return the error, or, + // * when we can recover from an error. + UNRECOVERABLE_ERROR = 4; + } + + // Required. The unique identifier of the conversation this notification + // refers to. + // Format: `projects//conversations/`. + string conversation = 1; + + // Required. The type of the event that this notification refers to. + Type type = 2; + + // Optional. More detailed information about an error. Only set for type + // UNRECOVERABLE_ERROR_IN_PHONE_CALL. + google.rpc.Status error_status = 3; + + // Payload of conversation event. + oneof payload { + // Payload of NEW_MESSAGE event. + Message new_message_payload = 4; + } +} diff --git a/google/cloud/dialogflow_v2beta1/proto/conversation_profile.proto b/google/cloud/dialogflow_v2beta1/proto/conversation_profile.proto new file mode 100644 index 000000000..4d7513736 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/proto/conversation_profile.proto @@ -0,0 +1,564 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2beta1; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/cloud/dialogflow/v2beta1/audio_config.proto"; +import "google/cloud/dialogflow/v2beta1/document.proto"; +import "google/cloud/dialogflow/v2beta1/participant.proto"; +import "google/longrunning/operations.proto"; +import "google/protobuf/empty.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/timestamp.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2beta1"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2beta1;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "ConversationProfileProto"; +option java_package = "com.google.cloud.dialogflow.v2beta1"; +option objc_class_prefix = "DF"; + +// Service for managing [ConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfile]. +service ConversationProfiles { + option (google.api.default_host) = "dialogflow.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/dialogflow"; + + // Returns the list of all conversation profiles in the specified project. + rpc ListConversationProfiles(ListConversationProfilesRequest) returns (ListConversationProfilesResponse) { + option (google.api.http) = { + get: "/v2beta1/{parent=projects/*}/conversationProfiles" + additional_bindings { + get: "/v2beta1/{parent=projects/*/locations/*}/conversationProfiles" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Retrieves the specified conversation profile. + rpc GetConversationProfile(GetConversationProfileRequest) returns (ConversationProfile) { + option (google.api.http) = { + get: "/v2beta1/{name=projects/*/conversationProfiles/*}" + additional_bindings { + get: "/v2beta1/{name=projects/*/locations/*/conversationProfiles/*}" + } + }; + option (google.api.method_signature) = "name"; + } + + // Creates a conversation profile in the specified project. + // + // [ConversationProfile.CreateTime][] and [ConversationProfile.UpdateTime][] + // aren't populated in the response. You can retrieve them via + // [GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile] API. + rpc CreateConversationProfile(CreateConversationProfileRequest) returns (ConversationProfile) { + option (google.api.http) = { + post: "/v2beta1/{parent=projects/*}/conversationProfiles" + body: "conversation_profile" + additional_bindings { + post: "/v2beta1/{parent=projects/*/locations/*}/conversationProfiles" + body: "conversation_profile" + } + }; + option (google.api.method_signature) = "parent,conversation_profile"; + } + + // Updates the specified conversation profile. + // + // [ConversationProfile.CreateTime][] and [ConversationProfile.UpdateTime][] + // aren't populated in the response. You can retrieve them via + // [GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile] API. + rpc UpdateConversationProfile(UpdateConversationProfileRequest) returns (ConversationProfile) { + option (google.api.http) = { + patch: "/v2beta1/{conversation_profile.name=projects/*/conversationProfiles/*}" + body: "conversation_profile" + additional_bindings { + patch: "/v2beta1/{conversation_profile.name=projects/*/locations/*/conversationProfiles/*}" + body: "conversation_profile" + } + }; + option (google.api.method_signature) = "conversation_profile,update_mask"; + } + + // Deletes the specified conversation profile. + rpc DeleteConversationProfile(DeleteConversationProfileRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/v2beta1/{name=projects/*/conversationProfiles/*}" + additional_bindings { + delete: "/v2beta1/{name=projects/*/locations/*/conversationProfiles/*}" + } + }; + option (google.api.method_signature) = "name"; + } +} + +// Defines the services to connect to incoming Dialogflow conversations. +message ConversationProfile { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/ConversationProfile" + pattern: "projects/{project}/conversationProfiles/{conversation_profile}" + pattern: "projects/{project}/locations/{location}/conversationProfiles/{conversation_profile}" + }; + + // The unique identifier of this conversation profile. + // Format: `projects//locations//conversationProfiles/`. + string name = 1; + + // Required. Human readable name for this profile. Max length 1024 bytes. + string display_name = 2 [(google.api.field_behavior) = REQUIRED]; + + // Output only. Create time of the conversation profile. + google.protobuf.Timestamp create_time = 11 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. Update time of the conversation profile. + google.protobuf.Timestamp update_time = 12 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Configuration for an automated agent to use with this profile. + AutomatedAgentConfig automated_agent_config = 3; + + // Configuration for agent assistance to use with this profile. + HumanAgentAssistantConfig human_agent_assistant_config = 4; + + // Configuration for connecting to a live agent. + HumanAgentHandoffConfig human_agent_handoff_config = 5; + + // Configuration for publishing conversation lifecycle events. + NotificationConfig notification_config = 6; + + // Configuration for logging conversation lifecycle events. + LoggingConfig logging_config = 7; + + // Configuration for publishing new message events. Event will be sent in + // format of [ConversationEvent][google.cloud.dialogflow.v2beta1.ConversationEvent] + NotificationConfig new_message_event_notification_config = 8; + + // Settings for speech transcription. + SpeechToTextConfig stt_config = 9; + + // Language code for the conversation profile. If not specified, the language + // is en-US. Language at ConversationProfile should be set for all non en-us + // languages. + string language_code = 10; +} + +// Defines the Automated Agent to connect to a conversation. +message AutomatedAgentConfig { + // Required. ID of the Dialogflow agent environment to use. + // + // This project needs to either be the same project as the conversation or you + // need to grant `service-@gcp-sa-dialogflow.iam.gserviceaccount.com` the `Dialogflow API + // Service Agent` role in this project. + // + // - For ES agents, use format: `projects//locations//agent/environments/`. If environment is not + // specified, the default `draft` environment is used. Refer to + // [DetectIntentRequest](/dialogflow/docs/reference/rpc/google.cloud.dialogflow.v2beta1#google.cloud.dialogflow.v2beta1.DetectIntentRequest) + // for more details. + // + // - For CX agents, use format `projects//locations//agents//environments/`. If environment is not specified, the default `draft` environment + // is used. + string agent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Agent" + } + ]; +} + +// Defines the Human Agent Assistant to connect to a conversation. +message HumanAgentAssistantConfig { + // Settings of suggestion trigger. + message SuggestionTriggerSettings { + // Do not trigger if last utterance is small talk. + bool no_small_talk = 1; + + // Only trigger suggestion if participant role of last utterance is + // END_USER. + bool only_end_user = 2; + } + + // Config for suggestion features. + message SuggestionFeatureConfig { + // The suggestion feature. + SuggestionFeature suggestion_feature = 5; + + // Automatically iterates all participants and tries to compile + // suggestions. + // + // Supported features: ARTICLE_SUGGESTION, FAQ, DIALOGFLOW_ASSIST. + bool enable_event_based_suggestion = 3; + + // Settings of suggestion trigger. + // + // Currently, only ARTICLE_SUGGESTION, FAQ, and DIALOGFLOW_ASSIST will use + // this field. + SuggestionTriggerSettings suggestion_trigger_settings = 10; + + // Configs of query. + SuggestionQueryConfig query_config = 6; + + // Configs of custom conversation model. + ConversationModelConfig conversation_model_config = 7; + } + + // Detail human agent assistant config. + message SuggestionConfig { + // Configuration of different suggestion features. One feature can have only + // one config. + repeated SuggestionFeatureConfig feature_configs = 2; + + // If `group_suggestion_responses` is false, and there are multiple + // `feature_configs` in `event based suggestion` or + // StreamingAnalyzeContent, we will try to deliver suggestions to customers + // as soon as we get new suggestion. Different type of suggestions based on + // the same context will be in separate Pub/Sub event or + // `StreamingAnalyzeContentResponse`. + // + // If `group_suggestion_responses` set to true. All the suggestions to the + // same participant based on the same context will be grouped into a single + // Pub/Sub event or StreamingAnalyzeContentResponse. + bool group_suggestion_responses = 3; + } + + // Config for suggestion query. + message SuggestionQueryConfig { + // Knowledge base source settings. + // + // Supported features: ARTICLE_SUGGESTION, FAQ. + message KnowledgeBaseQuerySource { + // Required. Knowledge bases to query. Format: + // `projects//locations//knowledgeBases/`. Currently, only one knowledge + // base is supported. + repeated string knowledge_bases = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/KnowledgeBase" + } + ]; + } + + // Document source settings. + // + // Supported features: SMART_REPLY, SMART_COMPOSE. + message DocumentQuerySource { + // Required. Knowledge documents to query from. Format: + // `projects//locations//knowledgeBases//documents/`. + // Currently, only one document is supported. + repeated string documents = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Document" + } + ]; + } + + // Dialogflow source setting. + // + // Supported feature: DIALOGFLOW_ASSIST. + message DialogflowQuerySource { + // Required. The name of a dialogflow virtual agent used for end user side intent + // detection and suggestion. Format: `projects//locations//agent`. When multiple agents are allowed in + // the same Dialogflow project. + string agent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Agent" + } + ]; + } + + // Settings that determine how to filter recent conversation context when + // generating suggestions. + message ContextFilterSettings { + // If set to true, the last message from virtual agent (hand off message) + // and the message before it (trigger message of hand off) are dropped. + bool drop_handoff_messages = 1; + + // If set to true, all messages from virtual agent are dropped. + bool drop_virtual_agent_messages = 2; + + // If set to true, all messages from ivr stage are dropped. + bool drop_ivr_messages = 3; + } + + // Source of query. + oneof query_source { + // Query from knowledgebase. It is used by: + // ARTICLE_SUGGESTION, FAQ. + KnowledgeBaseQuerySource knowledge_base_query_source = 1; + + // Query from knowledge base document. It is used by: + // SMART_REPLY, SMART_COMPOSE. + DocumentQuerySource document_query_source = 2; + + // Query from Dialogflow agent. It is used by DIALOGFLOW_ASSIST. + DialogflowQuerySource dialogflow_query_source = 3; + } + + // Maximum number of results to return. Currently, if unset, defaults to 10. + // And the max number is 20. + int32 max_results = 4; + + // Confidence threshold of query result. + // + // Agent Assist gives each suggestion a score in the range [0.0, 1.0], based + // on the relevance between the suggestion and the current conversation + // context. A score of 0.0 has no relevance, while a score of 1.0 has high + // relevance. Only suggestions with a score greater than or equal to the + // value of this field are included in the results. + // + // For a baseline model (the default), the recommended value is in the range + // [0.05, 0.1]. + // + // For a custom model, there is no recommended value. Tune this value by + // starting from a very low value and slowly increasing until you have + // desired results. + // + // If this field is not set, it is default to 0.0, which means that all + // suggestions are returned. + // + // Supported features: ARTICLE_SUGGESTION, FAQ, SMART_REPLY, SMART_COMPOSE. + float confidence_threshold = 5; + + // Determines how recent conversation context is filtered when generating + // suggestions. If unspecified, no messages will be dropped. + ContextFilterSettings context_filter_settings = 7; + } + + // Custom conversation models used in agent assist feature. + // + // Supported feature: ARTICLE_SUGGESTION, SMART_COMPOSE, SMART_REPLY. + message ConversationModelConfig { + // Required. Conversation model resource name. Format: `projects//conversationModels/`. + string model = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/ConversationModel" + } + ]; + } + + // Configuration for analyses to run on each conversation message. + message MessageAnalysisConfig { + // Enable entity extraction in conversation messages on [agent assist + // stage](https://cloud.google.com/dialogflow/priv/docs/contact-center/basics#stages). + // If unspecified, defaults to false. + bool enable_entity_extraction = 2; + + // Enable sentiment analysis in conversation messages on [agent assist + // stage](https://cloud.google.com/dialogflow/priv/docs/contact-center/basics#stages). + // If unspecified, defaults to false. Sentiment analysis inspects user input + // and identifies the prevailing subjective opinion, especially to determine + // a user's attitude as positive, negative, or neutral: + // https://cloud.google.com/natural-language/docs/basics#sentiment_analysis + // For [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.StreamingAnalyzeContent] method, result will be in + // [StreamingAnalyzeContentResponse.message.SentimentAnalysisResult][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentResponse.message]. + // For [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent] method, result will be in + // [AnalyzeContentResponse.message.SentimentAnalysisResult][google.cloud.dialogflow.v2beta1.AnalyzeContentResponse.message] + // For [Conversations.ListMessages][google.cloud.dialogflow.v2beta1.Conversations.ListMessages] method, result will be in + // [ListMessagesResponse.messages.SentimentAnalysisResult][google.cloud.dialogflow.v2beta1.ListMessagesResponse.messages] + // If Pub/Sub notification is configured, result will be in + // [ConversationEvent.new_message_payload.SentimentAnalysisResult][google.cloud.dialogflow.v2beta1.ConversationEvent.new_message_payload]. + bool enable_sentiment_analysis = 3; + } + + // Pub/Sub topic on which to publish new agent assistant events. + NotificationConfig notification_config = 2; + + // Configuration for agent assistance of human agent participant. + SuggestionConfig human_agent_suggestion_config = 3; + + // Configuration for agent assistance of end user participant. + SuggestionConfig end_user_suggestion_config = 4; + + // Configuration for message analysis. + MessageAnalysisConfig message_analysis_config = 5; +} + +// Defines the hand off to a live agent, typically on which external agent +// service provider to connect to a conversation. +message HumanAgentHandoffConfig { + // Configuration specific to LivePerson (https://www.liveperson.com). + message LivePersonConfig { + // Required. Account number of the LivePerson account to connect. This is + // the account number you input at the login page. + string account_number = 1 [(google.api.field_behavior) = REQUIRED]; + } + + // Configuration specific to Salesforce Live Agent. + message SalesforceLiveAgentConfig { + // Required. The organization ID of the Salesforce account. + string organization_id = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. Live Agent deployment ID. + string deployment_id = 2 [(google.api.field_behavior) = REQUIRED]; + + // Required. Live Agent chat button ID. + string button_id = 3 [(google.api.field_behavior) = REQUIRED]; + + // Required. Domain of the Live Agent endpoint for this agent. You can find + // the endpoint URL in the `Live Agent settings` page. For example if URL + // has the form https://d.la4-c2-phx.salesforceliveagent.com/..., + // you should fill in d.la4-c2-phx.salesforceliveagent.com. + string endpoint_domain = 4 [(google.api.field_behavior) = REQUIRED]; + } + + // Required. Specifies which agent service to connect for human agent handoff. + oneof agent_service { + // Uses LivePerson (https://www.liveperson.com). + LivePersonConfig live_person_config = 1; + + // Uses Salesforce Live Agent. + SalesforceLiveAgentConfig salesforce_live_agent_config = 2; + } +} + +// Defines notification behavior. +message NotificationConfig { + // Format of cloud pub/sub message. + enum MessageFormat { + // If it is unspecified, PROTO will be used. + MESSAGE_FORMAT_UNSPECIFIED = 0; + + // Pubsub message will be serialized proto. + PROTO = 1; + + // Pubsub message will be json. + JSON = 2; + } + + // Name of the Pub/Sub topic to publish conversation + // events like + // [CONVERSATION_STARTED][google.cloud.dialogflow.v2beta1.ConversationEvent.Type.CONVERSATION_STARTED] as + // serialized [ConversationEvent][google.cloud.dialogflow.v2beta1.ConversationEvent] protos. + // + // Notification works for phone calls, if this topic either is in the same + // project as the conversation or you grant `service-@gcp-sa-dialogflow.iam.gserviceaccount.com` the `Dialogflow Service + // Agent` role in the topic project. + // + // Format: `projects//locations//topics/`. + string topic = 1; + + // Format of message. + MessageFormat message_format = 2; +} + +// Defines logging behavior for conversation lifecycle events. +message LoggingConfig { + // Whether to log conversation events like + // [CONVERSATION_STARTED][google.cloud.dialogflow.v2beta1.ConversationEvent.Type.CONVERSATION_STARTED] to + // Stackdriver in the conversation project as JSON format + // [ConversationEvent][google.cloud.dialogflow.v2beta1.ConversationEvent] protos. + bool enable_stackdriver_logging = 3; +} + +// The request message for [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfiles.ListConversationProfiles]. +message ListConversationProfilesRequest { + // Required. The project to list all conversation profiles from. + // Format: `projects//locations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/ConversationProfile" + } + ]; + + // The maximum number of items to return in a single page. By + // default 100 and at most 1000. + int32 page_size = 2; + + // The next_page_token value returned from a previous list request. + string page_token = 3; +} + +// The response message for [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfiles.ListConversationProfiles]. +message ListConversationProfilesResponse { + // The list of project conversation profiles. There is a maximum number + // of items returned based on the page_size field in the request. + repeated ConversationProfile conversation_profiles = 1; + + // Token to retrieve the next page of results, or empty if there are no + // more results in the list. + string next_page_token = 2; +} + +// The request message for [ConversationProfiles.GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile]. +message GetConversationProfileRequest { + // Required. The resource name of the conversation profile. + // Format: `projects//locations//conversationProfiles/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/ConversationProfile" + } + ]; +} + +// The request message for [ConversationProfiles.CreateConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.CreateConversationProfile]. +message CreateConversationProfileRequest { + // Required. The project to create a conversation profile for. + // Format: `projects//locations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/ConversationProfile" + } + ]; + + // Required. The conversation profile to create. + ConversationProfile conversation_profile = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request message for [ConversationProfiles.UpdateConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.UpdateConversationProfile]. +message UpdateConversationProfileRequest { + // Required. The conversation profile to update. + ConversationProfile conversation_profile = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The mask to control which fields to update. + google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request message for [ConversationProfiles.DeleteConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.DeleteConversationProfile]. +// +// This operation fails if the conversation profile is still referenced from +// a phone number. +message DeleteConversationProfileRequest { + // Required. The name of the conversation profile to delete. + // Format: `projects//locations//conversationProfiles/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/ConversationProfile" + } + ]; +} diff --git a/google/cloud/dialogflow_v2beta1/proto/document.proto b/google/cloud/dialogflow_v2beta1/proto/document.proto index a0bc4812a..0af6eb9e6 100644 --- a/google/cloud/dialogflow_v2beta1/proto/document.proto +++ b/google/cloud/dialogflow_v2beta1/proto/document.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -100,6 +100,22 @@ service Documents { }; } + // Create documents by importing data from external sources. + rpc ImportDocuments(ImportDocumentsRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v2beta1/{parent=projects/*/knowledgeBases/*}/documents:import" + body: "*" + additional_bindings { + post: "/v2beta1/{parent=projects/*/locations/*/knowledgeBases/*}/documents:import" + body: "*" + } + }; + option (google.longrunning.operation_info) = { + response_type: "ImportDocumentsResponse" + metadata_type: "KnowledgeOperationMetadata" + }; + } + // Deletes the specified document. // // Note: The `projects.agent.knowledgeBases.documents` resource is deprecated; @@ -219,6 +235,13 @@ message Document { // Documents for which unstructured text is extracted and used for // question answering. EXTRACTIVE_QA = 2; + + // The entire document content as a whole can be used for query results. + // Only for Contact Center Solutions on Dialogflow. + ARTICLE_SUGGESTION = 3; + + // The legacy enum for agent-facing smart reply feature. + SMART_REPLY = 4; } // Optional. The document resource name. @@ -283,6 +306,12 @@ message Document { // This reload may have been triggered automatically or manually // and may not have succeeded. ReloadStatus latest_reload_status = 12 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Optional. Metadata for the document. The metadata supports arbitrary + // key-value pairs. Suggested use cases include storing a document's title, + // an external URL distinct from the document's content_uri, etc. + // The max size of a `key` or a `value` of the metadata is 1024 bytes. + map metadata = 7 [(google.api.field_behavior) = OPTIONAL]; } // Request message for [Documents.GetDocument][google.cloud.dialogflow.v2beta1.Documents.GetDocument]. @@ -371,6 +400,62 @@ message CreateDocumentRequest { bool import_gcs_custom_metadata = 3; } +// Request message for [Documents.ImportDocuments][google.cloud.dialogflow.v2beta1.Documents.ImportDocuments]. +message ImportDocumentsRequest { + // Required. The knowledge base to import documents into. + // Format: `projects//locations//knowledgeBases/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Document" + } + ]; + + // Required. The source to use for importing documents. + // + // If the source captures multiple objects, then multiple documents will be + // created, one corresponding to each object, and all of these documents will + // be created using the same document template. + oneof source { + // The Google Cloud Storage location for the documents. + // The path can include a wildcard. + // + // These URIs may have the forms + // `gs:///`. + // `gs:////*.`. + GcsSources gcs_source = 2; + } + + // Required. Document template used for importing all the documents. + ImportDocumentTemplate document_template = 3 [(google.api.field_behavior) = REQUIRED]; + + // Whether to import custom metadata from Google Cloud Storage. + // Only valid when the document source is Google Cloud Storage URI. + bool import_gcs_custom_metadata = 4; +} + +// The template used for importing documents. +message ImportDocumentTemplate { + // Required. The MIME type of the document. + string mime_type = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The knowledge type of document content. + repeated Document.KnowledgeType knowledge_types = 2 [(google.api.field_behavior) = REQUIRED]; + + // Metadata for the document. The metadata supports arbitrary + // key-value pairs. Suggested use cases include storing a document's title, + // an external URL distinct from the document's content_uri, etc. + // The max size of a `key` or a `value` of the metadata is 1024 bytes. + map metadata = 3; +} + +// Response message for [Documents.ImportDocuments][google.cloud.dialogflow.v2beta1.Documents.ImportDocuments]. +message ImportDocumentsResponse { + // Includes details about skipped documents or any other warnings. + repeated google.rpc.Status warnings = 1; +} + // Request message for [Documents.DeleteDocument][google.cloud.dialogflow.v2beta1.Documents.DeleteDocument]. message DeleteDocumentRequest { // Required. The name of the document to delete. diff --git a/google/cloud/dialogflow_v2beta1/proto/entity_type.proto b/google/cloud/dialogflow_v2beta1/proto/entity_type.proto index e1e5f8ef1..a1703f1be 100644 --- a/google/cloud/dialogflow_v2beta1/proto/entity_type.proto +++ b/google/cloud/dialogflow_v2beta1/proto/entity_type.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/google/cloud/dialogflow_v2beta1/proto/environment.proto b/google/cloud/dialogflow_v2beta1/proto/environment.proto index 49efe6ff5..a933b9bea 100644 --- a/google/cloud/dialogflow_v2beta1/proto/environment.proto +++ b/google/cloud/dialogflow_v2beta1/proto/environment.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/google/cloud/dialogflow_v2beta1/proto/gcs.proto b/google/cloud/dialogflow_v2beta1/proto/gcs.proto index 4750a0626..f01ad76f3 100644 --- a/google/cloud/dialogflow_v2beta1/proto/gcs.proto +++ b/google/cloud/dialogflow_v2beta1/proto/gcs.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -27,6 +27,15 @@ option java_outer_classname = "GcsProto"; option java_package = "com.google.cloud.dialogflow.v2beta1"; option objc_class_prefix = "DF"; +// Google Cloud Storage locations for the inputs. +message GcsSources { + // Required. Google Cloud Storage URIs for the inputs. A URI is of the + // form: + // gs://bucket/object-prefix-or-name + // Whether a prefix or name is used depends on the use case. + repeated string uris = 2 [(google.api.field_behavior) = REQUIRED]; +} + // Google Cloud Storage location for single input. message GcsSource { // Required. The Google Cloud Storage URIs for the inputs. A URI is of the diff --git a/google/cloud/dialogflow_v2beta1/proto/human_agent_assistant_event.proto b/google/cloud/dialogflow_v2beta1/proto/human_agent_assistant_event.proto new file mode 100644 index 000000000..d49c83acb --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/proto/human_agent_assistant_event.proto @@ -0,0 +1,51 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2beta1; + +import "google/cloud/dialogflow/v2beta1/participant.proto"; +import "google/api/annotations.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2beta1"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2beta1;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "HumanAgentAssistantEventProto"; +option java_package = "com.google.cloud.dialogflow.v2beta1"; +option objc_class_prefix = "DF"; + +// Output only. Represents a notification sent to Pub/Sub subscribers for +// agent assistant events in a specific conversation. +message HumanAgentAssistantEvent { + // The conversation this notification refers to. + // Format: `projects//conversations/`. + string conversation = 1; + + // The participant that the suggestion is compiled for. And This field is used + // to call [Participants.ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] API. Format: + // `projects//conversations//participants/`. + // It will not be set in legacy workflow. + // [HumanAgentAssistantConfig.name][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.name] for more + // information. + string participant = 3; + + // The suggestion results payload that this notification refers to. It will + // only be set when + // [HumanAgentAssistantConfig.SuggestionConfig.group_suggestion_responses][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.SuggestionConfig.group_suggestion_responses] + // sets to true. + repeated SuggestionResult suggestion_results = 5; +} diff --git a/google/cloud/dialogflow_v2beta1/proto/intent.proto b/google/cloud/dialogflow_v2beta1/proto/intent.proto index 58c3b31f8..ba8c1f830 100644 --- a/google/cloud/dialogflow_v2beta1/proto/intent.proto +++ b/google/cloud/dialogflow_v2beta1/proto/intent.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -50,6 +50,9 @@ service Intents { additional_bindings { get: "/v2beta1/{parent=projects/*/locations/*/agent}/intents" } + additional_bindings { + get: "/v2beta1/{parent=projects/*/agent/environments/*}/intents" + } }; option (google.api.method_signature) = "parent"; option (google.api.method_signature) = "parent,language_code"; @@ -1114,6 +1117,12 @@ message Intent { // auto-markup in the UI is turned off. bool ml_disabled = 19 [(google.api.field_behavior) = OPTIONAL]; + // Optional. Indicates that a live agent should be brought in to handle the + // interaction with the user. In most cases, when you set this flag to true, + // you would also want to set end_interaction to true as well. Default is + // false. + bool live_agent_handoff = 20 [(google.api.field_behavior) = OPTIONAL]; + // Optional. Indicates that this intent ends an interaction. Some integrations // (e.g., Actions on Google or Dialogflow phone gateway) use this information // to close interaction with an end user. Default is false. diff --git a/google/cloud/dialogflow_v2beta1/proto/knowledge_base.proto b/google/cloud/dialogflow_v2beta1/proto/knowledge_base.proto index 997b2bea0..fe50073bc 100644 --- a/google/cloud/dialogflow_v2beta1/proto/knowledge_base.proto +++ b/google/cloud/dialogflow_v2beta1/proto/knowledge_base.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/google/cloud/dialogflow_v2beta1/proto/participant.proto b/google/cloud/dialogflow_v2beta1/proto/participant.proto new file mode 100644 index 000000000..afe0c9faf --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/proto/participant.proto @@ -0,0 +1,1255 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.cloud.dialogflow.v2beta1; + +import "google/api/annotations.proto"; +import "google/api/client.proto"; +import "google/api/field_behavior.proto"; +import "google/api/resource.proto"; +import "google/cloud/dialogflow/v2beta1/audio_config.proto"; +import "google/cloud/dialogflow/v2beta1/session.proto"; +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/struct.proto"; +import "google/protobuf/timestamp.proto"; +import "google/rpc/status.proto"; + +option cc_enable_arenas = true; +option csharp_namespace = "Google.Cloud.Dialogflow.V2beta1"; +option go_package = "google.golang.org/genproto/googleapis/cloud/dialogflow/v2beta1;dialogflow"; +option java_multiple_files = true; +option java_outer_classname = "ParticipantProto"; +option java_package = "com.google.cloud.dialogflow.v2beta1"; +option objc_class_prefix = "DF"; + +// Service for managing [Participants][google.cloud.dialogflow.v2beta1.Participant]. +service Participants { + option (google.api.default_host) = "dialogflow.googleapis.com"; + option (google.api.oauth_scopes) = + "https://www.googleapis.com/auth/cloud-platform," + "https://www.googleapis.com/auth/dialogflow"; + + // Creates a new participant in a conversation. + rpc CreateParticipant(CreateParticipantRequest) returns (Participant) { + option (google.api.http) = { + post: "/v2beta1/{parent=projects/*/conversations/*}/participants" + body: "participant" + additional_bindings { + post: "/v2beta1/{parent=projects/*/locations/*/conversations/*}/participants" + body: "participant" + } + }; + option (google.api.method_signature) = "parent,participant"; + } + + // Retrieves a conversation participant. + rpc GetParticipant(GetParticipantRequest) returns (Participant) { + option (google.api.http) = { + get: "/v2beta1/{name=projects/*/conversations/*/participants/*}" + additional_bindings { + get: "/v2beta1/{name=projects/*/locations/*/conversations/*/participants/*}" + } + }; + option (google.api.method_signature) = "name"; + } + + // Returns the list of all participants in the specified conversation. + rpc ListParticipants(ListParticipantsRequest) returns (ListParticipantsResponse) { + option (google.api.http) = { + get: "/v2beta1/{parent=projects/*/conversations/*}/participants" + additional_bindings { + get: "/v2beta1/{parent=projects/*/locations/*/conversations/*}/participants" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Updates the specified participant. + rpc UpdateParticipant(UpdateParticipantRequest) returns (Participant) { + option (google.api.http) = { + patch: "/v2beta1/{participant.name=projects/*/conversations/*/participants/*}" + body: "participant" + additional_bindings { + patch: "/v2beta1/{participant.name=projects/*/locations/*/conversations/*/participants/*}" + body: "participant" + } + }; + option (google.api.method_signature) = "participant,update_mask"; + } + + // Adds a text (chat, for example), or audio (phone recording, for example) + // message from a participant into the conversation. + // + // Note: Always use agent versions for production traffic + // sent to virtual agents. See [Versions and + // environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + rpc AnalyzeContent(AnalyzeContentRequest) returns (AnalyzeContentResponse) { + option (google.api.http) = { + post: "/v2beta1/{participant=projects/*/conversations/*/participants/*}:analyzeContent" + body: "*" + additional_bindings { + post: "/v2beta1/{participant=projects/*/locations/*/conversations/*/participants/*}:analyzeContent" + body: "*" + } + }; + option (google.api.method_signature) = "participant,text_input"; + option (google.api.method_signature) = "participant,audio_input"; + option (google.api.method_signature) = "participant,event_input"; + } + + // Adds a text (e.g., chat) or audio (e.g., phone recording) message from a + // participant into the conversation. + // Note: This method is only available through the gRPC API (not REST). + // + // The top-level message sent to the client by the server is + // `StreamingAnalyzeContentResponse`. Multiple response messages can be + // returned in order. The first one or more messages contain the + // `recognition_result` field. Each result represents a more complete + // transcript of what the user said. The next message contains the + // `reply_text` field, and potentially the `reply_audio` and/or the + // `automated_agent_reply` fields. + // + // Note: Always use agent versions for production traffic + // sent to virtual agents. See [Versions and + // environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + rpc StreamingAnalyzeContent(stream StreamingAnalyzeContentRequest) returns (stream StreamingAnalyzeContentResponse) { + } + + // Gets suggested articles for a participant based on specific historical + // messages. + // + // Note that [ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] will only list the auto-generated + // suggestions, while [CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion] will try to compile suggestion + // based on the provided conversation context in the real time. + rpc SuggestArticles(SuggestArticlesRequest) returns (SuggestArticlesResponse) { + option (google.api.http) = { + post: "/v2beta1/{parent=projects/*/conversations/*/participants/*}/suggestions:suggestArticles" + body: "*" + additional_bindings { + post: "/v2beta1/{parent=projects/*/locations/*/conversations/*/participants/*}/suggestions:suggestArticles" + body: "*" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Gets suggested faq answers for a participant based on specific historical + // messages. + rpc SuggestFaqAnswers(SuggestFaqAnswersRequest) returns (SuggestFaqAnswersResponse) { + option (google.api.http) = { + post: "/v2beta1/{parent=projects/*/conversations/*/participants/*}/suggestions:suggestFaqAnswers" + body: "*" + additional_bindings { + post: "/v2beta1/{parent=projects/*/locations/*/conversations/*/participants/*}/suggestions:suggestFaqAnswers" + body: "*" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Gets smart replies for a participant based on specific historical + // messages. + rpc SuggestSmartReplies(SuggestSmartRepliesRequest) returns (SuggestSmartRepliesResponse) { + option (google.api.http) = { + post: "/v2beta1/{parent=projects/*/conversations/*/participants/*}/suggestions:suggestSmartReplies" + body: "*" + additional_bindings { + post: "/v2beta1/{parent=projects/*/locations/*/conversations/*/participants/*}/suggestions:suggestSmartReplies" + body: "*" + } + }; + option (google.api.method_signature) = "parent"; + } + + // Deprecated: Use inline suggestion, event based suggestion or + // Suggestion* API instead. + // See [HumanAgentAssistantConfig.name][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.name] for more + // details. + // Removal Date: 2020-09-01. + // + // Retrieves suggestions for live agents. + // + // This method should be used by human agent client software to fetch auto + // generated suggestions in real-time, while the conversation with an end user + // is in progress. The functionality is implemented in terms of the + // [list pagination](/apis/design/design_patterns#list_pagination) + // design pattern. The client app should use the `next_page_token` field + // to fetch the next batch of suggestions. `suggestions` are sorted by + // `create_time` in descending order. + // To fetch latest suggestion, just set `page_size` to 1. + // To fetch new suggestions without duplication, send request with filter + // `create_time_epoch_microseconds > [first item's create_time of previous + // request]` and empty page_token. + rpc ListSuggestions(ListSuggestionsRequest) returns (ListSuggestionsResponse) { + option deprecated = true; + option (google.api.http) = { + get: "/v2beta1/{parent=projects/*/conversations/*/participants/*}/suggestions" + }; + } + + // Deprecated. use [SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles] and [SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers] instead. + // + // Gets suggestions for a participant based on specific historical + // messages. + // + // Note that [ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] will only list the auto-generated + // suggestions, while [CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion] will try to compile suggestion + // based on the provided conversation context in the real time. + rpc CompileSuggestion(CompileSuggestionRequest) returns (CompileSuggestionResponse) { + option deprecated = true; + option (google.api.http) = { + post: "/v2beta1/{parent=projects/*/conversations/*/participants/*}/suggestions:compile" + body: "*" + }; + } +} + +// Represents a conversation participant (human agent, virtual agent, end-user). +message Participant { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/Participant" + pattern: "projects/{project}/conversations/{conversation}/participants/{participant}" + pattern: "projects/{project}/locations/{location}/conversations/{conversation}/participants/{participant}" + }; + + // Enumeration of the roles a participant can play in a conversation. + enum Role { + // Participant role not set. + ROLE_UNSPECIFIED = 0; + + // Participant is a human agent. + HUMAN_AGENT = 1; + + // Participant is an automated agent, such as a Dialogflow agent. + AUTOMATED_AGENT = 2; + + // Participant is an end user that has called or chatted with + // Dialogflow services. + END_USER = 3; + } + + // Optional. The unique identifier of this participant. + // Format: `projects//locations//conversations//participants/`. + string name = 1 [(google.api.field_behavior) = OPTIONAL]; + + // Immutable. The role this participant plays in the conversation. This field must be set + // during participant creation and is then immutable. + Role role = 2 [(google.api.field_behavior) = IMMUTABLE]; + + // Optional. Obfuscated user id that should be associated with the created participant. + // + // You can specify a user id as follows: + // + // 1. If you set this field in + // [CreateParticipantRequest][google.cloud.dialogflow.v2beta1.CreateParticipantRequest.participant] or + // [UpdateParticipantRequest][google.cloud.dialogflow.v2beta1.UpdateParticipantRequest.participant], + // Dialogflow adds the obfuscated user id with the participant. + // + // 2. If you set this field in + // [AnalyzeContent][google.cloud.dialogflow.v2beta1.AnalyzeContentRequest.obfuscated_external_user_id] or + // [StreamingAnalyzeContent][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.obfuscated_external_user_id], + // Dialogflow will update [Participant.obfuscated_external_user_id][google.cloud.dialogflow.v2beta1.Participant.obfuscated_external_user_id]. + // + // Dialogflow uses this user id for following purposes: + // 1) Billing and measurement. If user with the same + // obfuscated_external_user_id is created in a later conversation, dialogflow + // will know it's the same user. 2) Agent assist suggestion personalization. + // For example, Dialogflow can use it to provide personalized smart reply + // suggestions for this user. + // + // Note: + // + // * Please never pass raw user ids to Dialogflow. Always obfuscate your user + // id first. + // * Dialogflow only accepts a UTF-8 encoded string, e.g., a hex digest of a + // hash function like SHA-512. + // * The length of the user id must be <= 256 characters. + string obfuscated_external_user_id = 7 [(google.api.field_behavior) = OPTIONAL]; +} + +// Represents a message posted into a conversation. +message Message { + option (google.api.resource) = { + type: "dialogflow.googleapis.com/Message" + pattern: "projects/{project}/conversations/{conversation}/messages/{message}" + pattern: "projects/{project}/locations/{location}/conversations/{conversation}/messages/{message}" + }; + + // Optional. The unique identifier of the message. + // Format: `projects//locations//conversations//messages/`. + string name = 1 [(google.api.field_behavior) = OPTIONAL]; + + // Required. The message content. + string content = 2 [(google.api.field_behavior) = REQUIRED]; + + // Optional. The message language. + // This should be a [BCP-47](https://www.rfc-editor.org/rfc/bcp/bcp47.txt) + // language tag. Example: "en-US". + string language_code = 3 [(google.api.field_behavior) = OPTIONAL]; + + // Output only. The participant that sends this message. + string participant = 4 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The role of the participant. + Participant.Role participant_role = 5 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The time when the message was created in Contact Center AI. + google.protobuf.Timestamp create_time = 6 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Optional. The time when the message was sent. + google.protobuf.Timestamp send_time = 9 [(google.api.field_behavior) = OPTIONAL]; + + // Output only. The annotation for the message. + MessageAnnotation message_annotation = 7 [(google.api.field_behavior) = OUTPUT_ONLY]; + + // Output only. The sentiment analysis result for the message. + SentimentAnalysisResult sentiment_analysis = 8 [(google.api.field_behavior) = OUTPUT_ONLY]; +} + +// The request message for [Participants.CreateParticipant][google.cloud.dialogflow.v2beta1.Participants.CreateParticipant]. +message CreateParticipantRequest { + // Required. Resource identifier of the conversation adding the participant. + // Format: `projects//locations//conversations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Participant" + } + ]; + + // Required. The participant to create. + Participant participant = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// The request message for [Participants.GetParticipant][google.cloud.dialogflow.v2beta1.Participants.GetParticipant]. +message GetParticipantRequest { + // Required. The name of the participant. Format: + // `projects//locations//conversations//participants/`. + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Participant" + } + ]; +} + +// The request message for [Participants.ListParticipants][google.cloud.dialogflow.v2beta1.Participants.ListParticipants]. +message ListParticipantsRequest { + // Required. The conversation to list all participants from. + // Format: `projects//locations//conversations/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + child_type: "dialogflow.googleapis.com/Participant" + } + ]; + + // Optional. The maximum number of items to return in a single page. By + // default 100 and at most 1000. + int32 page_size = 2 [(google.api.field_behavior) = OPTIONAL]; + + // Optional. The next_page_token value returned from a previous list request. + string page_token = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// The response message for [Participants.ListParticipants][google.cloud.dialogflow.v2beta1.Participants.ListParticipants]. +message ListParticipantsResponse { + // The list of participants. There is a maximum number of items + // returned based on the page_size field in the request. + repeated Participant participants = 1; + + // Token to retrieve the next page of results or empty if there are no + // more results in the list. + string next_page_token = 2; +} + +// The request message for [Participants.UpdateParticipant][google.cloud.dialogflow.v2beta1.Participants.UpdateParticipant]. +message UpdateParticipantRequest { + // Required. The participant to update. + Participant participant = 1 [(google.api.field_behavior) = REQUIRED]; + + // Required. The mask to specify which fields to update. + google.protobuf.FieldMask update_mask = 2 [(google.api.field_behavior) = REQUIRED]; +} + +// Represents the natural language text to be processed. +message InputText { + // Required. The UTF-8 encoded natural language text to be processed. + // Text length must not exceed 256 bytes. + string text = 1; + + // Required. The language of this conversational query. See [Language + // Support](https://cloud.google.com/dialogflow/docs/reference/language) + // for a list of the currently supported language codes. + string language_code = 2; +} + +// Represents the natural language speech audio to be processed. +message InputAudio { + // Required. Instructs the speech recognizer how to process the speech audio. + InputAudioConfig config = 1; + + // Required. The natural language speech audio to be processed. + // A single request can contain up to 1 minute of speech audio data. + // The transcribed text cannot contain more than 256 bytes. + bytes audio = 2; +} + +// Represents the natural language speech audio to be processed. +message AudioInput { + // Required. Instructs the speech recognizer how to process the speech audio. + InputAudioConfig config = 1; + + // Required. The natural language speech audio to be processed. + // A single request can contain up to 1 minute of speech audio data. + // The transcribed text cannot contain more than 256 bytes. + bytes audio = 2; +} + +// Represents the natural language speech audio to be played to the end user. +message OutputAudio { + // Required. Instructs the speech synthesizer how to generate the speech + // audio. + OutputAudioConfig config = 1; + + // Required. The natural language speech audio. + bytes audio = 2; +} + +// Represents a response from an automated agent. +message AutomatedAgentReply { + // Required. + oneof response { + // Response of the Dialogflow [Sessions.DetectIntent][google.cloud.dialogflow.v2beta1.Sessions.DetectIntent] call. + DetectIntentResponse detect_intent_response = 1; + } + + // Response messages from the automated agent. + repeated ResponseMessage response_messages = 3; + + // Info on the query match for the automated agent response. + oneof match { + // Name of the intent if an intent is matched for the query. + // For a V2 query, the value format is `projects//locations/ + // /agent/intents/`. + // For a V3 query, the value format is `projects//locations/ + // /agents//intents/`. + string intent = 4 [(google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Intent" + }]; + + // Event name if an event is triggered for the query. + string event = 5; + } + + // The collection of current Dialogflow CX agent session parameters at the + // time of this response. + google.protobuf.Struct cx_session_parameters = 6; +} + +// The type of Human Agent Assistant API suggestion to perform, and the maximum +// number of results to return for that type. Multiple `Feature` objects can +// be specified in the `features` list. +message SuggestionFeature { + // Defines the type of Human Agent Assistant feature. + enum Type { + // Unspecified feature type. + TYPE_UNSPECIFIED = 0; + + // Run article suggestion model. + ARTICLE_SUGGESTION = 1; + + // Run FAQ model. + FAQ = 2; + + // Run smart reply model. + SMART_REPLY = 3; + } + + // Type of Human Agent Assistant API feature to request. + Type type = 1; +} + +// The request message for [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent]. +message AnalyzeContentRequest { + // Required. The name of the participant this text comes from. + // Format: `projects//locations//conversations//participants/`. + string participant = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Participant" + } + ]; + + // Required. The input content. + oneof input { + // The natural language text to be processed. + InputText text = 3 [deprecated = true]; + + // The natural language speech audio to be processed. + InputAudio audio = 4 [deprecated = true]; + + // The natural language text to be processed. + TextInput text_input = 6; + + // The natural language speech audio to be processed. + AudioInput audio_input = 7; + + // An input event to send to Dialogflow. + EventInput event_input = 8; + } + + // Speech synthesis configuration. + // The speech synthesis settings for a virtual agent that may be configured + // for the associated conversation profile are not used when calling + // AnalyzeContent. If this configuration is not supplied, speech synthesis + // is disabled. + OutputAudioConfig reply_audio_config = 5; + + // Parameters for a Dialogflow virtual-agent query. + QueryParameters query_params = 9; + + // Optional. The send time of the message from end user or human agent's + // perspective. It is used for identifying the same message under one + // participant. + // + // Given two messages under the same participant: + // - If send time are different regardless of whether the content of the + // messages are exactly the same, the conversation will regard them as + // two distinct messages sent by the participant. + // - If send time is the same regardless of whether the content of the + // messages are exactly the same, the conversation will regard them as + // same message, and ignore the message received later. + // + // If the value is not provided, a new request will always be regarded as a + // new message without any de-duplication. + google.protobuf.Timestamp message_send_time = 10; + + // A unique identifier for this request. Restricted to 36 ASCII characters. + // A random UUID is recommended. + // This request is only idempotent if a `request_id` is provided. + string request_id = 11; +} + +// The message in the response that indicates the parameters of DTMF. +message DtmfParameters { + // Indicates whether DTMF input can be handled in the next request. + bool accepts_dtmf_input = 1; +} + +// The response message for [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent]. +message AnalyzeContentResponse { + // Output only. The output text content. + // This field is set if the automated agent responded with text to show to + // the user. + string reply_text = 1; + + // Optional. The audio data bytes encoded as specified in the request. + // This field is set if: + // + // - `reply_audio_config` was specified in the request, or + // - The automated agent responded with audio to play to the user. In such + // case, `reply_audio.config` contains settings used to synthesize the + // speech. + // + // 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. + OutputAudio reply_audio = 2; + + // Optional. Only set if a Dialogflow automated agent has responded. + // Note that: [AutomatedAgentReply.detect_intent_response.output_audio][] + // and [AutomatedAgentReply.detect_intent_response.output_audio_config][] + // are always empty, use [reply_audio][google.cloud.dialogflow.v2beta1.AnalyzeContentResponse.reply_audio] instead. + AutomatedAgentReply automated_agent_reply = 3; + + // Output only. Message analyzed by CCAI. + Message message = 5; + + // The suggestions for most recent human agent. The order is the same as + // [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] of + // [HumanAgentAssistantConfig.human_agent_suggestion_config][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.human_agent_suggestion_config]. + repeated SuggestionResult human_agent_suggestion_results = 6; + + // The suggestions for end user. The order is the same as + // [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] of + // [HumanAgentAssistantConfig.end_user_suggestion_config][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.end_user_suggestion_config]. + repeated SuggestionResult end_user_suggestion_results = 7; + + // Indicates the parameters of DTMF. + DtmfParameters dtmf_parameters = 9; +} + +// Defines the language used in the input text. +message InputTextConfig { + // Required. The language of this conversational query. See [Language + // Support](https://cloud.google.com/dialogflow/docs/reference/language) + // for a list of the currently supported language codes. + string language_code = 1; +} + +// The top-level message sent by the client to the +// [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.StreamingAnalyzeContent] method. +// +// Multiple request messages should be sent in order: +// +// 1. The first message must contain +// [participant][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.participant], +// [config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] and optionally +// [query_params][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.query_params]. If you want +// to receive an audio response, it should also contain +// [reply_audio_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.reply_audio_config]. +// The message must not contain +// [input][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input]. +// +// 2. If [config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] in the first message +// was set to [audio_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.audio_config], +// all subsequent messages must contain +// [input_audio][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_audio] to continue +// with Speech recognition. +// If you decide to rather analyze text input after you already started +// Speech recognition, please send a message with +// [StreamingAnalyzeContentRequest.input_text][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_text]. +// +// However, note that: +// +// * Dialogflow will bill you for the audio so far. +// * Dialogflow discards all Speech recognition results in favor of the +// text input. +// +// 3. If [StreamingAnalyzeContentRequest.config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] in the first message was set +// to [StreamingAnalyzeContentRequest.text_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.text_config], then the second message +// must contain only [input_text][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_text]. +// Moreover, you must not send more than two messages. +// +// After you sent all input, you must half-close or abort the request stream. +message StreamingAnalyzeContentRequest { + // Required. The name of the participant this text comes from. + // Format: `projects//locations//conversations//participants/`. + string participant = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Participant" + } + ]; + + // Required. The input config. + oneof config { + // Instructs the speech recognizer how to process the speech audio. + InputAudioConfig audio_config = 2; + + // The natural language text to be processed. + InputTextConfig text_config = 3; + } + + // Speech synthesis configuration. + // The speech synthesis settings for a virtual agent that may be configured + // for the associated conversation profile are not used when calling + // StreamingAnalyzeContent. If this configuration is not supplied, speech + // synthesis is disabled. + OutputAudioConfig reply_audio_config = 4; + + // Required. The input. + oneof input { + // The input audio content to be recognized. Must be sent if `audio_config` + // is set in the first message. The complete audio over all streaming + // messages must not exceed 1 minute. + bytes input_audio = 5; + + // The UTF-8 encoded natural language text to be processed. Must be sent if + // `text_config` is set in the first message. Text length must not exceed + // 256 bytes. The `input_text` field can be only sent once. + string input_text = 6; + + // The DTMF digits used to invoke intent and fill in parameter value. + // + // This input is ignored if the previous response indicated that DTMF input + // is not accepted. + TelephonyDtmfEvents input_dtmf = 9; + } + + // Parameters for a Dialogflow virtual-agent query. + QueryParameters query_params = 7; + + // Enable full bidirectional streaming. You can keep streaming the audio until + // timeout, and there's no need to half close the stream to get the response. + // + // Restrictions: + // + // - Timeout: 3 mins. + // - Audio Encoding: only supports [AudioEncoding.AUDIO_ENCODING_LINEAR_16][google.cloud.dialogflow.v2beta1.AudioEncoding.AUDIO_ENCODING_LINEAR_16] + // and [AudioEncoding.AUDIO_ENCODING_MULAW][google.cloud.dialogflow.v2beta1.AudioEncoding.AUDIO_ENCODING_MULAW] + // - Lifecycle: conversation should be in `Assist Stage`, go to + // [Conversation.CreateConversation][] for more information. + // + // InvalidArgument Error will be returned if the one of restriction checks + // failed. + // + // You can find more details in + // https://cloud.google.com/dialogflow/priv/docs/agent-assist/analyze-content-streaming + bool enable_extended_streaming = 11; +} + +// The top-level message returned from the `StreamingAnalyzeContent` 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 next message contains `reply_text` and optionally `reply_audio` +// returned by an agent. This message may also contain +// `automated_agent_reply`. +message StreamingAnalyzeContentResponse { + // The result of speech recognition. + StreamingRecognitionResult recognition_result = 1; + + // Optional. The output text content. + // This field is set if an automated agent responded with a text for the user. + string reply_text = 2; + + // Optional. The audio data bytes encoded as specified in the request. + // This field is set if: + // + // - The `reply_audio_config` field is specified in the request. + // - The automated agent, which this output comes from, responded with audio. + // In such case, the `reply_audio.config` field contains settings used to + // synthesize the speech. + // + // 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. + OutputAudio reply_audio = 3; + + // Optional. Only set if a Dialogflow automated agent has responded. + // Note that: [AutomatedAgentReply.detect_intent_response.output_audio][] + // and [AutomatedAgentReply.detect_intent_response.output_audio_config][] + // are always empty, use [reply_audio][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentResponse.reply_audio] instead. + AutomatedAgentReply automated_agent_reply = 4; + + // Output only. Message analyzed by CCAI. + Message message = 6; + + // The suggestions for most recent human agent. The order is the same as + // [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] of + // [HumanAgentAssistantConfig.human_agent_suggestion_config][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.human_agent_suggestion_config]. + repeated SuggestionResult human_agent_suggestion_results = 7; + + // The suggestions for end user. The order is the same as + // [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] of + // [HumanAgentAssistantConfig.end_user_suggestion_config][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.end_user_suggestion_config]. + repeated SuggestionResult end_user_suggestion_results = 8; + + // Indicates the parameters of DTMF. + DtmfParameters dtmf_parameters = 10; +} + +// Represents a part of a message possibly annotated with an entity. The part +// can be an entity or purely a part of the message between two entities or +// message start/end. +message AnnotatedMessagePart { + // Required. A part of a message possibly annotated with an entity. + string text = 1; + + // Optional. The [Dialogflow system entity + // type](https://cloud.google.com/dialogflow/docs/reference/system-entities) + // of this message part. If this is empty, Dialogflow could not annotate the + // phrase part with a system entity. + string entity_type = 2; + + // Optional. The [Dialogflow system entity formatted value + // ](https://cloud.google.com/dialogflow/docs/reference/system-entities) of + // this message part. For example for a system entity of type + // `@sys.unit-currency`, this may contain: + //
+  // {
+  //   "amount": 5,
+  //   "currency": "USD"
+  // }
+  // 
+ google.protobuf.Value formatted_value = 3; +} + +// Represents the result of annotation for the message. +message MessageAnnotation { + // Optional. The collection of annotated message parts ordered by their + // position in the message. You can recover the annotated message by + // concatenating [AnnotatedMessagePart.text]. + repeated AnnotatedMessagePart parts = 1; + + // Required. Indicates whether the text message contains entities. + bool contain_entities = 2; +} + +// Represents article answer. +message ArticleAnswer { + // The article title. + string title = 1; + + // The article URI. + string uri = 2; + + // Output only. Article snippets. + repeated string snippets = 3; + + // A map that contains metadata about the answer and the + // document from which it originates. + map metadata = 5; + + // The name of answer record, in the format of + // "projects//locations//answerRecords/" + string answer_record = 6; +} + +// Represents answer from "frequently asked questions". +message FaqAnswer { + // The piece of text from the `source` knowledge base document. + string answer = 1; + + // The system's confidence score that this Knowledge answer is a good match + // for this conversational query, range from 0.0 (completely uncertain) + // to 1.0 (completely certain). + float confidence = 2; + + // The corresponding FAQ question. + string question = 3; + + // Indicates which Knowledge Document this answer was extracted + // from. + // Format: `projects//locations//agent/knowledgeBases//documents/`. + string source = 4; + + // A map that contains metadata about the answer and the + // document from which it originates. + map metadata = 5; + + // The name of answer record, in the format of + // "projects//locations//answerRecords/" + string answer_record = 6; +} + +// Represents a smart reply answer. +message SmartReplyAnswer { + // The content of the reply. + string reply = 1; + + // Smart reply confidence. + // The system's confidence score that this reply is a good match for + // this conversation, as a value from 0.0 (completely uncertain) to 1.0 + // (completely certain). + float confidence = 2; + + // The name of answer record, in the format of + // "projects//locations//answerRecords/" + string answer_record = 3; +} + +// One response of different type of suggestion response which is used in +// the response of [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent] and +// [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent], as well as [HumanAgentAssistantEvent][google.cloud.dialogflow.v2beta1.HumanAgentAssistantEvent]. +message SuggestionResult { + // Different type of suggestion response. + oneof suggestion_response { + // Error status if the request failed. + google.rpc.Status error = 1; + + // SuggestArticlesResponse if request is for ARTICLE_SUGGESTION. + SuggestArticlesResponse suggest_articles_response = 2; + + // SuggestFaqAnswersResponse if request is for FAQ_ANSWER. + SuggestFaqAnswersResponse suggest_faq_answers_response = 3; + + // SuggestSmartRepliesResponse if request is for SMART_REPLY. + SuggestSmartRepliesResponse suggest_smart_replies_response = 4; + } +} + +// The request message for [Participants.SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles]. +message SuggestArticlesRequest { + // Required. The name of the participant to fetch suggestion for. + // Format: `projects//locations//conversations//participants/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Participant" + } + ]; + + // Optional. The name of the latest conversation message to compile suggestion + // for. If empty, it will be the latest message of the conversation. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2 [ + (google.api.field_behavior) = OPTIONAL, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Message" + } + ]; + + // Optional. Max number of messages prior to and including + // [latest_message][google.cloud.dialogflow.v2beta1.SuggestArticlesRequest.latest_message] to use as context + // when compiling the suggestion. By default 20 and at most 50. + int32 context_size = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// The response message for [Participants.SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles]. +message SuggestArticlesResponse { + // Output only. Articles ordered by score in descending order. + repeated ArticleAnswer article_answers = 1; + + // The name of the latest conversation message used to compile + // suggestion for. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2; + + // Number of messages prior to and including + // [latest_message][google.cloud.dialogflow.v2beta1.SuggestArticlesResponse.latest_message] to compile the + // suggestion. It may be smaller than the + // [SuggestArticlesResponse.context_size][google.cloud.dialogflow.v2beta1.SuggestArticlesResponse.context_size] field in the request if there + // aren't that many messages in the conversation. + int32 context_size = 3; +} + +// The request message for [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers]. +message SuggestFaqAnswersRequest { + // Required. The name of the participant to fetch suggestion for. + // Format: `projects//locations//conversations//participants/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Participant" + } + ]; + + // Optional. The name of the latest conversation message to compile suggestion + // for. If empty, it will be the latest message of the conversation. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2 [ + (google.api.field_behavior) = OPTIONAL, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Message" + } + ]; + + // Optional. Max number of messages prior to and including + // [latest_message] to use as context when compiling the + // suggestion. By default 20 and at most 50. + int32 context_size = 3 [(google.api.field_behavior) = OPTIONAL]; +} + +// The request message for [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers]. +message SuggestFaqAnswersResponse { + // Output only. Answers extracted from FAQ documents. + repeated FaqAnswer faq_answers = 1; + + // The name of the latest conversation message used to compile + // suggestion for. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2; + + // Number of messages prior to and including + // [latest_message][google.cloud.dialogflow.v2beta1.SuggestFaqAnswersResponse.latest_message] to compile the + // suggestion. It may be smaller than the + // [SuggestFaqAnswersRequest.context_size][google.cloud.dialogflow.v2beta1.SuggestFaqAnswersRequest.context_size] field in the request if there + // aren't that many messages in the conversation. + int32 context_size = 3; +} + +// The request message for [Participants.SuggestSmartReplies][google.cloud.dialogflow.v2beta1.Participants.SuggestSmartReplies]. +message SuggestSmartRepliesRequest { + // Required. The name of the participant to fetch suggestion for. + // Format: `projects//locations//conversations//participants/`. + string parent = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Participant" + } + ]; + + // The current natural language text segment to compile suggestion + // for. This provides a way for user to get follow up smart reply suggestion + // after a smart reply selection, without sending a text message. + TextInput current_text_input = 4; + + // The name of the latest conversation message to compile suggestion + // for. If empty, it will be the latest message of the conversation. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2 [(google.api.resource_reference) = { + type: "dialogflow.googleapis.com/Message" + }]; + + // Optional. Max number of messages prior to and including + // [latest_message] to use as context when compiling the + // suggestion. By default 20 and at most 50. + int32 context_size = 3; +} + +// The response message for [Participants.SuggestSmartReplies][google.cloud.dialogflow.v2beta1.Participants.SuggestSmartReplies]. +message SuggestSmartRepliesResponse { + // Output only. Multiple reply options provided by smart reply service. The + // order is based on the rank of the model prediction. + // The maximum number of the returned replies is set in SmartReplyConfig. + repeated SmartReplyAnswer smart_reply_answers = 1; + + // The name of the latest conversation message used to compile + // suggestion for. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2; + + // Number of messages prior to and including + // [latest_message][google.cloud.dialogflow.v2beta1.SuggestSmartRepliesResponse.latest_message] to compile the + // suggestion. It may be smaller than the + // [SuggestSmartRepliesRequest.context_size][google.cloud.dialogflow.v2beta1.SuggestSmartRepliesRequest.context_size] field in the request if there + // aren't that many messages in the conversation. + int32 context_size = 3; +} + +// Represents a suggestion for a human agent. +message Suggestion { + option deprecated = true; + + // Represents suggested article. + message Article { + // Output only. The article title. + string title = 1; + + // Output only. The article URI. + string uri = 2; + + // Output only. Article snippets. + repeated string snippets = 3; + + // Output only. A map that contains metadata about the answer and the + // document from which it originates. + map metadata = 5; + + // Output only. The name of answer record, in the format of + // "projects//locations//answerRecords/" + string answer_record = 6; + } + + // Represents suggested answer from "frequently asked questions". + message FaqAnswer { + // Output only. The piece of text from the `source` knowledge base document. + string answer = 1; + + // The system's confidence score that this Knowledge answer is a good match + // for this conversational query, range from 0.0 (completely uncertain) + // to 1.0 (completely certain). + float confidence = 2; + + // Output only. The corresponding FAQ question. + string question = 3; + + // Output only. Indicates which Knowledge Document this answer was extracted + // from. + // Format: `projects//locations//agent/knowledgeBases//documents/`. + string source = 4; + + // Output only. A map that contains metadata about the answer and the + // document from which it originates. + map metadata = 5; + + // Output only. The name of answer record, in the format of + // "projects//locations//answerRecords/" + string answer_record = 6; + } + + // Output only. The name of this suggestion. + // Format: + // `projects//locations//conversations//participants/*/suggestions/`. + string name = 1; + + // Output only. Articles ordered by score in descending order. + repeated Article articles = 2; + + // Output only. Answers extracted from FAQ documents. + repeated FaqAnswer faq_answers = 4; + + // Output only. The time the suggestion was created. + google.protobuf.Timestamp create_time = 5; + + // Output only. Latest message used as context to compile this suggestion. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 7; +} + +// The request message for [Participants.ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions]. +message ListSuggestionsRequest { + option deprecated = true; + + // Required. The name of the participant to fetch suggestions for. + // Format: `projects//locations//conversations//participants/`. + string parent = 1; + + // Optional. The maximum number of items to return in a single page. The + // default value is 100; the maximum value is 1000. + int32 page_size = 2; + + // Optional. The next_page_token value returned from a previous list request. + string page_token = 3; + + // Optional. Filter on suggestions fields. Currently predicates on + // `create_time` and `create_time_epoch_microseconds` are supported. + // `create_time` only support milliseconds accuracy. E.g., + // `create_time_epoch_microseconds > 1551790877964485` or + // `create_time > 2017-01-15T01:30:15.01Z` + // + // For more information about filtering, see + // [API Filtering](https://aip.dev/160). + string filter = 4; +} + +// The response message for [Participants.ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions]. +message ListSuggestionsResponse { + option deprecated = true; + + // Required. The list of suggestions. There will be a maximum number of items + // returned based on the page_size field in the request. `suggestions` is + // sorted by `create_time` in descending order. + repeated Suggestion suggestions = 1; + + // Optional. Token to retrieve the next page of results or empty if there are + // no more results in the list. + string next_page_token = 2; +} + +// The request message for [Participants.CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion]. +message CompileSuggestionRequest { + option deprecated = true; + + // Required. The name of the participant to fetch suggestion for. + // Format: `projects//locations//conversations//participants/`. + string parent = 1; + + // Optional. The name of the latest conversation message to compile suggestion + // for. If empty, it will be the latest message of the conversation. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2; + + // Optional. Max number of messages prior to and including + // [latest_message] to use as context when compiling the + // suggestion. If zero or less than zero, 20 is used. + int32 context_size = 3; +} + +// The response message for [Participants.CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion]. +message CompileSuggestionResponse { + option deprecated = true; + + // The compiled suggestion. + Suggestion suggestion = 1; + + // The name of the latest conversation message used to compile + // suggestion for. + // + // Format: `projects//locations//conversations//messages/`. + string latest_message = 2; + + // Number of messages prior to and including + // [latest_message][google.cloud.dialogflow.v2beta1.CompileSuggestionResponse.latest_message] + // to compile the suggestion. It may be smaller than the + // [CompileSuggestionRequest.context_size][google.cloud.dialogflow.v2beta1.CompileSuggestionRequest.context_size] field in the request if + // there aren't that many messages in the conversation. + int32 context_size = 3; +} + +// Response messages from an automated agent. +message ResponseMessage { + // The text response message. + message Text { + // A collection of text responses. + repeated string text = 1; + } + + // Indicates that the conversation should be handed off to a human 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 of a CX 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. + message LiveAgentHandoff { + // Custom metadata for your handoff procedure. Dialogflow doesn't impose + // any structure on this. + google.protobuf.Struct metadata = 1; + } + + // Indicates that interaction with the Dialogflow agent has ended. + message EndInteraction { + + } + + // Required. The rich response message. + oneof message { + // Returns a text response. + Text text = 1; + + // Returns a response containing a custom, platform-specific payload. + google.protobuf.Struct payload = 2; + + // Hands off conversation to a live agent. + LiveAgentHandoff live_agent_handoff = 3; + + // A signal that indicates the interaction with the Dialogflow agent has + // ended. + EndInteraction end_interaction = 4; + } +} diff --git a/google/cloud/dialogflow_v2beta1/proto/session.proto b/google/cloud/dialogflow_v2beta1/proto/session.proto index 566824d5b..ab5e04523 100644 --- a/google/cloud/dialogflow_v2beta1/proto/session.proto +++ b/google/cloud/dialogflow_v2beta1/proto/session.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -108,7 +108,8 @@ message DetectIntentRequest { // ID>`, // // If `Location ID` is not specified we assume default 'us' location. If - // `Environment ID` is not specified, we assume default 'draft' environment. + // `Environment ID` is not specified, we assume default 'draft' environment + // (`Environment ID` might be referred to as environment name at some places). // If `User ID` is not specified, we are using "-". It's up to the API caller // to choose an appropriate `Session ID` and `User Id`. They can be a random // number or some type of user and session identifiers (preferably hashed). @@ -321,7 +322,8 @@ message QueryResult { // - 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 + // - Else: depending on parameter value type, could be one of string, + // number, boolean, null, list or map // - MapValue value: // - If parameter's entity type is a composite entity: // map from composite entity property names to property values @@ -740,7 +742,8 @@ message EventInput { // - 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 + // - Else: depending on parameter value type, could be one of string, + // number, boolean, null, list or map // - MapValue value: // - If parameter's entity type is a composite entity: // map from composite entity property names to property values diff --git a/google/cloud/dialogflow_v2beta1/proto/session_entity_type.proto b/google/cloud/dialogflow_v2beta1/proto/session_entity_type.proto index c80497aa0..c692a6b94 100644 --- a/google/cloud/dialogflow_v2beta1/proto/session_entity_type.proto +++ b/google/cloud/dialogflow_v2beta1/proto/session_entity_type.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/google/cloud/dialogflow_v2beta1/proto/validation_result.proto b/google/cloud/dialogflow_v2beta1/proto/validation_result.proto index 9ab491f7b..82352ad08 100644 --- a/google/cloud/dialogflow_v2beta1/proto/validation_result.proto +++ b/google/cloud/dialogflow_v2beta1/proto/validation_result.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ message ValidationError { // Not specified. This value should never be used. SEVERITY_UNSPECIFIED = 0; - // The agent doesn't follow Dialogflow best practicies. + // The agent doesn't follow Dialogflow best practices. INFO = 1; // The agent may not behave as expected. diff --git a/google/cloud/dialogflow_v2beta1/proto/webhook.proto b/google/cloud/dialogflow_v2beta1/proto/webhook.proto index 83f9d70a1..64c7efda7 100644 --- a/google/cloud/dialogflow_v2beta1/proto/webhook.proto +++ b/google/cloud/dialogflow_v2beta1/proto/webhook.proto @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -117,6 +117,12 @@ message WebhookResponse { // `fulfillment_messages`, and `payload` fields. EventInput followup_event_input = 6; + // Indicates that a live agent should be brought in to handle the + // interaction with the user. In most cases, when you set this flag to true, + // you would also want to set end_interaction to true as well. Default is + // false. + bool live_agent_handoff = 7; + // Optional. Indicates that this intent ends an interaction. Some integrations // (e.g., Actions on Google or Dialogflow phone gateway) use this information // to close interaction with an end user. Default is false. diff --git a/google/cloud/dialogflow_v2beta1/services/agents/async_client.py b/google/cloud/dialogflow_v2beta1/services/agents/async_client.py index 51bf46bfc..3ec1c0c33 100644 --- a/google/cloud/dialogflow_v2beta1/services/agents/async_client.py +++ b/google/cloud/dialogflow_v2beta1/services/agents/async_client.py @@ -74,7 +74,36 @@ class AgentsAsyncClient: 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 + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AgentsAsyncClient: The constructed client. + """ + return AgentsClient.from_service_account_info.__func__(AgentsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AgentsAsyncClient: The constructed client. + """ + return AgentsClient.from_service_account_file.__func__(AgentsAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -150,13 +179,14 @@ async def get_agent( r"""Retrieves the specified agent. Args: - request (:class:`~.agent.GetAgentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.GetAgentRequest`): The request object. The request message for [Agents.GetAgent][google.cloud.dialogflow.v2beta1.Agents.GetAgent]. parent (:class:`str`): Required. The project that the agent to fetch is associated with. Format: ``projects/`` or ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -168,19 +198,18 @@ async def get_agent( sent along with the request as metadata. Returns: - ~.agent.Agent: - A Dialogflow agent is a virtual agent that handles - conversations with your end-users. It is a natural - language understanding module that understands the - nuances of human language. Dialogflow translates - end-user text or audio during a conversation to - structured data that your apps and services can - understand. You design and build a Dialogflow agent to - handle the types of conversations required for your - system. - - For more information about agents, see the `Agent - guide `__. + google.cloud.dialogflow_v2beta1.types.Agent: + A Dialogflow agent is a virtual agent that handles conversations with your + end-users. It is a natural language understanding + module that understands the nuances of human + language. Dialogflow translates end-user text or + audio during a conversation to structured data that + your apps and services can understand. You design and + build a Dialogflow agent to handle the types of + conversations required for your system. + + For more information about agents, see the [Agent + guide](\ https://cloud.google.com/dialogflow/docs/agents-overview). """ # Create or coerce a protobuf request object. @@ -233,10 +262,10 @@ async def set_agent( r"""Creates/updates the specified agent. Args: - request (:class:`~.gcd_agent.SetAgentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.SetAgentRequest`): The request object. The request message for [Agents.SetAgent][google.cloud.dialogflow.v2beta1.Agents.SetAgent]. - agent (:class:`~.gcd_agent.Agent`): + agent (:class:`google.cloud.dialogflow_v2beta1.types.Agent`): Required. The agent to update. This corresponds to the ``agent`` field on the ``request`` instance; if ``request`` is provided, this @@ -249,19 +278,18 @@ async def set_agent( sent along with the request as metadata. Returns: - ~.gcd_agent.Agent: - A Dialogflow agent is a virtual agent that handles - conversations with your end-users. It is a natural - language understanding module that understands the - nuances of human language. Dialogflow translates - end-user text or audio during a conversation to - structured data that your apps and services can - understand. You design and build a Dialogflow agent to - handle the types of conversations required for your - system. - - For more information about agents, see the `Agent - guide `__. + google.cloud.dialogflow_v2beta1.types.Agent: + A Dialogflow agent is a virtual agent that handles conversations with your + end-users. It is a natural language understanding + module that understands the nuances of human + language. Dialogflow translates end-user text or + audio during a conversation to structured data that + your apps and services can understand. You design and + build a Dialogflow agent to handle the types of + conversations required for your system. + + For more information about agents, see the [Agent + guide](\ https://cloud.google.com/dialogflow/docs/agents-overview). """ # Create or coerce a protobuf request object. @@ -316,13 +344,14 @@ async def delete_agent( r"""Deletes the specified agent. Args: - request (:class:`~.agent.DeleteAgentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.DeleteAgentRequest`): The request object. The request message for [Agents.DeleteAgent][google.cloud.dialogflow.v2beta1.Agents.DeleteAgent]. parent (:class:`str`): Required. The project that the agent to delete is associated with. Format: ``projects/`` or ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -387,13 +416,14 @@ async def search_agents( Sub-Collections `__. Args: - request (:class:`~.agent.SearchAgentsRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.SearchAgentsRequest`): The request object. The request message for [Agents.SearchAgents][google.cloud.dialogflow.v2beta1.Agents.SearchAgents]. parent (:class:`str`): Required. The project to list agents from. Format: ``projects/`` or ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -405,7 +435,7 @@ async def search_agents( sent along with the request as metadata. Returns: - ~.pagers.SearchAgentsAsyncPager: + google.cloud.dialogflow_v2beta1.services.agents.pagers.SearchAgentsAsyncPager: The response message for [Agents.SearchAgents][google.cloud.dialogflow.v2beta1.Agents.SearchAgents]. @@ -472,13 +502,14 @@ async def train_agent( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.agent.TrainAgentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.TrainAgentRequest`): The request object. The request message for [Agents.TrainAgent][google.cloud.dialogflow.v2beta1.Agents.TrainAgent]. parent (:class:`str`): Required. The project that the agent to train is associated with. Format: ``projects/`` or ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -490,24 +521,22 @@ async def train_agent( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -571,13 +600,14 @@ async def export_agent( [ExportAgentResponse][google.cloud.dialogflow.v2beta1.ExportAgentResponse]> Args: - request (:class:`~.agent.ExportAgentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.ExportAgentRequest`): The request object. The request message for [Agents.ExportAgent][google.cloud.dialogflow.v2beta1.Agents.ExportAgent]. parent (:class:`str`): Required. The project that the agent to export is associated with. Format: ``projects/`` or ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -589,12 +619,12 @@ async def export_agent( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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 + :class:`google.cloud.dialogflow_v2beta1.types.ExportAgentResponse` + The response message for [Agents.ExportAgent][google.cloud.dialogflow.v2beta1.Agents.ExportAgent]. """ @@ -672,7 +702,7 @@ async def import_agent( draft agent is updated not when it is done training. Args: - request (:class:`~.agent.ImportAgentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.ImportAgentRequest`): The request object. The request message for [Agents.ImportAgent][google.cloud.dialogflow.v2beta1.Agents.ImportAgent]. @@ -683,24 +713,22 @@ async def import_agent( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -761,7 +789,7 @@ async def restore_agent( draft agent is updated not when it is done training. Args: - request (:class:`~.agent.RestoreAgentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.RestoreAgentRequest`): The request object. The request message for [Agents.RestoreAgent][google.cloud.dialogflow.v2beta1.Agents.RestoreAgent]. @@ -772,24 +800,22 @@ async def restore_agent( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -837,7 +863,7 @@ async def get_validation_result( automatically when training is completed. Args: - request (:class:`~.agent.GetValidationResultRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.GetValidationResultRequest`): The request object. The request message for [Agents.GetValidationResult][google.cloud.dialogflow.v2beta1.Agents.GetValidationResult]. @@ -848,7 +874,7 @@ async def get_validation_result( sent along with the request as metadata. Returns: - ~.validation_result.ValidationResult: + google.cloud.dialogflow_v2beta1.types.ValidationResult: Represents the output of agent validation. diff --git a/google/cloud/dialogflow_v2beta1/services/agents/client.py b/google/cloud/dialogflow_v2beta1/services/agents/client.py index dc8a6f743..656adc907 100644 --- a/google/cloud/dialogflow_v2beta1/services/agents/client.py +++ b/google/cloud/dialogflow_v2beta1/services/agents/client.py @@ -116,6 +116,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AgentsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -128,7 +144,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + AgentsClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -231,10 +247,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.AgentsTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -270,21 +286,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -327,7 +339,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -344,13 +356,14 @@ def get_agent( r"""Retrieves the specified agent. Args: - request (:class:`~.agent.GetAgentRequest`): + request (google.cloud.dialogflow_v2beta1.types.GetAgentRequest): The request object. The request message for [Agents.GetAgent][google.cloud.dialogflow.v2beta1.Agents.GetAgent]. - parent (:class:`str`): + parent (str): Required. The project that the agent to fetch is associated with. Format: ``projects/`` or ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -362,19 +375,18 @@ def get_agent( sent along with the request as metadata. Returns: - ~.agent.Agent: - A Dialogflow agent is a virtual agent that handles - conversations with your end-users. It is a natural - language understanding module that understands the - nuances of human language. Dialogflow translates - end-user text or audio during a conversation to - structured data that your apps and services can - understand. You design and build a Dialogflow agent to - handle the types of conversations required for your - system. - - For more information about agents, see the `Agent - guide `__. + google.cloud.dialogflow_v2beta1.types.Agent: + A Dialogflow agent is a virtual agent that handles conversations with your + end-users. It is a natural language understanding + module that understands the nuances of human + language. Dialogflow translates end-user text or + audio during a conversation to structured data that + your apps and services can understand. You design and + build a Dialogflow agent to handle the types of + conversations required for your system. + + For more information about agents, see the [Agent + guide](\ https://cloud.google.com/dialogflow/docs/agents-overview). """ # Create or coerce a protobuf request object. @@ -428,10 +440,10 @@ def set_agent( r"""Creates/updates the specified agent. Args: - request (:class:`~.gcd_agent.SetAgentRequest`): + request (google.cloud.dialogflow_v2beta1.types.SetAgentRequest): The request object. The request message for [Agents.SetAgent][google.cloud.dialogflow.v2beta1.Agents.SetAgent]. - agent (:class:`~.gcd_agent.Agent`): + agent (google.cloud.dialogflow_v2beta1.types.Agent): Required. The agent to update. This corresponds to the ``agent`` field on the ``request`` instance; if ``request`` is provided, this @@ -444,19 +456,18 @@ def set_agent( sent along with the request as metadata. Returns: - ~.gcd_agent.Agent: - A Dialogflow agent is a virtual agent that handles - conversations with your end-users. It is a natural - language understanding module that understands the - nuances of human language. Dialogflow translates - end-user text or audio during a conversation to - structured data that your apps and services can - understand. You design and build a Dialogflow agent to - handle the types of conversations required for your - system. - - For more information about agents, see the `Agent - guide `__. + google.cloud.dialogflow_v2beta1.types.Agent: + A Dialogflow agent is a virtual agent that handles conversations with your + end-users. It is a natural language understanding + module that understands the nuances of human + language. Dialogflow translates end-user text or + audio during a conversation to structured data that + your apps and services can understand. You design and + build a Dialogflow agent to handle the types of + conversations required for your system. + + For more information about agents, see the [Agent + guide](\ https://cloud.google.com/dialogflow/docs/agents-overview). """ # Create or coerce a protobuf request object. @@ -512,13 +523,14 @@ def delete_agent( r"""Deletes the specified agent. Args: - request (:class:`~.agent.DeleteAgentRequest`): + request (google.cloud.dialogflow_v2beta1.types.DeleteAgentRequest): The request object. The request message for [Agents.DeleteAgent][google.cloud.dialogflow.v2beta1.Agents.DeleteAgent]. - parent (:class:`str`): + parent (str): Required. The project that the agent to delete is associated with. Format: ``projects/`` or ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -584,13 +596,14 @@ def search_agents( Sub-Collections `__. Args: - request (:class:`~.agent.SearchAgentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.SearchAgentsRequest): The request object. The request message for [Agents.SearchAgents][google.cloud.dialogflow.v2beta1.Agents.SearchAgents]. - parent (:class:`str`): + parent (str): Required. The project to list agents from. Format: ``projects/`` or ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -602,7 +615,7 @@ def search_agents( sent along with the request as metadata. Returns: - ~.pagers.SearchAgentsPager: + google.cloud.dialogflow_v2beta1.services.agents.pagers.SearchAgentsPager: The response message for [Agents.SearchAgents][google.cloud.dialogflow.v2beta1.Agents.SearchAgents]. @@ -670,13 +683,14 @@ def train_agent( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.agent.TrainAgentRequest`): + request (google.cloud.dialogflow_v2beta1.types.TrainAgentRequest): The request object. The request message for [Agents.TrainAgent][google.cloud.dialogflow.v2beta1.Agents.TrainAgent]. - parent (:class:`str`): + parent (str): Required. The project that the agent to train is associated with. Format: ``projects/`` or ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -688,24 +702,22 @@ def train_agent( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -770,13 +782,14 @@ def export_agent( [ExportAgentResponse][google.cloud.dialogflow.v2beta1.ExportAgentResponse]> Args: - request (:class:`~.agent.ExportAgentRequest`): + request (google.cloud.dialogflow_v2beta1.types.ExportAgentRequest): The request object. The request message for [Agents.ExportAgent][google.cloud.dialogflow.v2beta1.Agents.ExportAgent]. - parent (:class:`str`): + parent (str): Required. The project that the agent to export is associated with. Format: ``projects/`` or ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -788,12 +801,12 @@ def export_agent( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.agent.ExportAgentResponse``: The response - message for + :class:`google.cloud.dialogflow_v2beta1.types.ExportAgentResponse` + The response message for [Agents.ExportAgent][google.cloud.dialogflow.v2beta1.Agents.ExportAgent]. """ @@ -872,7 +885,7 @@ def import_agent( draft agent is updated not when it is done training. Args: - request (:class:`~.agent.ImportAgentRequest`): + request (google.cloud.dialogflow_v2beta1.types.ImportAgentRequest): The request object. The request message for [Agents.ImportAgent][google.cloud.dialogflow.v2beta1.Agents.ImportAgent]. @@ -883,24 +896,22 @@ def import_agent( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -962,7 +973,7 @@ def restore_agent( draft agent is updated not when it is done training. Args: - request (:class:`~.agent.RestoreAgentRequest`): + request (google.cloud.dialogflow_v2beta1.types.RestoreAgentRequest): The request object. The request message for [Agents.RestoreAgent][google.cloud.dialogflow.v2beta1.Agents.RestoreAgent]. @@ -973,24 +984,22 @@ def restore_agent( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1039,7 +1048,7 @@ def get_validation_result( automatically when training is completed. Args: - request (:class:`~.agent.GetValidationResultRequest`): + request (google.cloud.dialogflow_v2beta1.types.GetValidationResultRequest): The request object. The request message for [Agents.GetValidationResult][google.cloud.dialogflow.v2beta1.Agents.GetValidationResult]. @@ -1050,7 +1059,7 @@ def get_validation_result( sent along with the request as metadata. Returns: - ~.validation_result.ValidationResult: + google.cloud.dialogflow_v2beta1.types.ValidationResult: Represents the output of agent validation. diff --git a/google/cloud/dialogflow_v2beta1/services/agents/pagers.py b/google/cloud/dialogflow_v2beta1/services/agents/pagers.py index e7d3832c9..e3c037634 100644 --- a/google/cloud/dialogflow_v2beta1/services/agents/pagers.py +++ b/google/cloud/dialogflow_v2beta1/services/agents/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2beta1.types import agent @@ -24,7 +33,7 @@ class SearchAgentsPager: """A pager for iterating through ``search_agents`` requests. This class thinly wraps an initial - :class:`~.agent.SearchAgentsResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.SearchAgentsResponse` object, and provides an ``__iter__`` method to iterate through its ``agents`` field. @@ -33,7 +42,7 @@ class SearchAgentsPager: through the ``agents`` field on the corresponding responses. - All the usual :class:`~.agent.SearchAgentsResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.SearchAgentsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.agent.SearchAgentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.SearchAgentsRequest): The initial request object. - response (:class:`~.agent.SearchAgentsResponse`): + response (google.cloud.dialogflow_v2beta1.types.SearchAgentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class SearchAgentsAsyncPager: """A pager for iterating through ``search_agents`` requests. This class thinly wraps an initial - :class:`~.agent.SearchAgentsResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.SearchAgentsResponse` object, and provides an ``__aiter__`` method to iterate through its ``agents`` field. @@ -95,7 +104,7 @@ class SearchAgentsAsyncPager: through the ``agents`` field on the corresponding responses. - All the usual :class:`~.agent.SearchAgentsResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.SearchAgentsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.agent.SearchAgentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.SearchAgentsRequest): The initial request object. - response (:class:`~.agent.SearchAgentsResponse`): + response (google.cloud.dialogflow_v2beta1.types.SearchAgentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2beta1/services/agents/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/agents/transports/grpc.py index a263af5b0..99193b309 100644 --- a/google/cloud/dialogflow_v2beta1/services/agents/transports/grpc.py +++ b/google/cloud/dialogflow_v2beta1/services/agents/transports/grpc.py @@ -63,6 +63,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -93,6 +94,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -109,6 +114,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -118,11 +128,6 @@ def __init__( 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 @@ -166,12 +171,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/agents/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/agents/transports/grpc_asyncio.py index 8553cd410..2ddc8ca85 100644 --- a/google/cloud/dialogflow_v2beta1/services/agents/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2beta1/services/agents/transports/grpc_asyncio.py @@ -107,6 +107,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -138,6 +139,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -154,6 +159,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -163,11 +173,6 @@ def __init__( 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 @@ -211,12 +216,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/answer_records/__init__.py b/google/cloud/dialogflow_v2beta1/services/answer_records/__init__.py new file mode 100644 index 000000000..7618f55a7 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/answer_records/__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 AnswerRecordsClient +from .async_client import AnswerRecordsAsyncClient + +__all__ = ( + "AnswerRecordsClient", + "AnswerRecordsAsyncClient", +) diff --git a/google/cloud/dialogflow_v2beta1/services/answer_records/async_client.py b/google/cloud/dialogflow_v2beta1/services/answer_records/async_client.py new file mode 100644 index 000000000..dfeb91746 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/answer_records/async_client.py @@ -0,0 +1,470 @@ +# -*- 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.dialogflow_v2beta1.services.answer_records import pagers +from google.cloud.dialogflow_v2beta1.types import answer_record +from google.cloud.dialogflow_v2beta1.types import answer_record as gcd_answer_record +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import AnswerRecordsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import AnswerRecordsGrpcAsyncIOTransport +from .client import AnswerRecordsClient + + +class AnswerRecordsAsyncClient: + """Service for managing + [AnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecord]. + """ + + _client: AnswerRecordsClient + + DEFAULT_ENDPOINT = AnswerRecordsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = AnswerRecordsClient.DEFAULT_MTLS_ENDPOINT + + answer_record_path = staticmethod(AnswerRecordsClient.answer_record_path) + parse_answer_record_path = staticmethod( + AnswerRecordsClient.parse_answer_record_path + ) + + common_billing_account_path = staticmethod( + AnswerRecordsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + AnswerRecordsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(AnswerRecordsClient.common_folder_path) + parse_common_folder_path = staticmethod( + AnswerRecordsClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + AnswerRecordsClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + AnswerRecordsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(AnswerRecordsClient.common_project_path) + parse_common_project_path = staticmethod( + AnswerRecordsClient.parse_common_project_path + ) + + common_location_path = staticmethod(AnswerRecordsClient.common_location_path) + parse_common_location_path = staticmethod( + AnswerRecordsClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AnswerRecordsAsyncClient: The constructed client. + """ + return AnswerRecordsClient.from_service_account_info.__func__(AnswerRecordsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AnswerRecordsAsyncClient: The constructed client. + """ + return AnswerRecordsClient.from_service_account_file.__func__(AnswerRecordsAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> AnswerRecordsTransport: + """Return the transport used by the client instance. + + Returns: + AnswerRecordsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(AnswerRecordsClient).get_transport_class, type(AnswerRecordsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, AnswerRecordsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the answer records 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, ~.AnswerRecordsTransport]): 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 = AnswerRecordsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def get_answer_record( + self, + request: answer_record.GetAnswerRecordRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> answer_record.AnswerRecord: + r"""Deprecated. + Retrieves a specific answer record. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.GetAnswerRecordRequest`): + The request object. Request message for + [AnswerRecords.GetAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.GetAnswerRecord]. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.AnswerRecord: + Answer records are records to manage answer history and feedbacks for + Dialogflow. + + Currently, answer record includes: + + - human agent assistant article suggestion + - human agent assistant faq article + + It doesn't include: + + - DetectIntent intent matching + - DetectIntent knowledge + + Answer records are not related to the conversation + history in the Dialogflow Console. A Record is + generated even when the end-user disables + conversation history in the console. Records are + created when there's a human agent assistant + suggestion generated. + + A typical workflow for customers provide feedback to + an answer is: + + 1. For human agent assistant, customers get + suggestion via ListSuggestions API. Together with + the answers, + [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] + are returned to the customers. + 2. The customer uses the + [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] + to call the [UpdateAnswerRecord][] method to send + feedback about a specific answer that they believe + is wrong. + + """ + # Create or coerce a protobuf request object. + + request = answer_record.GetAnswerRecordRequest(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.get_answer_record, + 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 list_answer_records( + self, + request: answer_record.ListAnswerRecordsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListAnswerRecordsAsyncPager: + r"""Returns the list of all answer records in the + specified project in reverse chronological order. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.ListAnswerRecordsRequest`): + The request object. Request message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecords.ListAnswerRecords]. + parent (:class:`str`): + Required. The project to list all answer records for in + reverse chronological order. 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: + google.cloud.dialogflow_v2beta1.services.answer_records.pagers.ListAnswerRecordsAsyncPager: + Response message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecords.ListAnswerRecords]. + + 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 = answer_record.ListAnswerRecordsRequest(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_answer_records, + 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.ListAnswerRecordsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def update_answer_record( + self, + request: gcd_answer_record.UpdateAnswerRecordRequest = None, + *, + answer_record: gcd_answer_record.AnswerRecord = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_answer_record.AnswerRecord: + r"""Updates the specified answer record. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.UpdateAnswerRecordRequest`): + The request object. Request message for + [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.UpdateAnswerRecord]. + answer_record (:class:`google.cloud.dialogflow_v2beta1.types.AnswerRecord`): + Required. Answer record to update. + This corresponds to the ``answer_record`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.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: + google.cloud.dialogflow_v2beta1.types.AnswerRecord: + Answer records are records to manage answer history and feedbacks for + Dialogflow. + + Currently, answer record includes: + + - human agent assistant article suggestion + - human agent assistant faq article + + It doesn't include: + + - DetectIntent intent matching + - DetectIntent knowledge + + Answer records are not related to the conversation + history in the Dialogflow Console. A Record is + generated even when the end-user disables + conversation history in the console. Records are + created when there's a human agent assistant + suggestion generated. + + A typical workflow for customers provide feedback to + an answer is: + + 1. For human agent assistant, customers get + suggestion via ListSuggestions API. Together with + the answers, + [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] + are returned to the customers. + 2. The customer uses the + [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] + to call the [UpdateAnswerRecord][] method to send + feedback about a specific answer that they believe + is wrong. + + """ + # Create or coerce a 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([answer_record, 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 = gcd_answer_record.UpdateAnswerRecordRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if answer_record is not None: + request.answer_record = answer_record + 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_answer_record, + 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( + (("answer_record.name", request.answer_record.name),) + ), + ) + + # 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("AnswerRecordsAsyncClient",) diff --git a/google/cloud/dialogflow_v2beta1/services/answer_records/client.py b/google/cloud/dialogflow_v2beta1/services/answer_records/client.py new file mode 100644 index 000000000..08e56aa52 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/answer_records/client.py @@ -0,0 +1,642 @@ +# -*- 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.dialogflow_v2beta1.services.answer_records import pagers +from google.cloud.dialogflow_v2beta1.types import answer_record +from google.cloud.dialogflow_v2beta1.types import answer_record as gcd_answer_record +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import AnswerRecordsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import AnswerRecordsGrpcTransport +from .transports.grpc_asyncio import AnswerRecordsGrpcAsyncIOTransport + + +class AnswerRecordsClientMeta(type): + """Metaclass for the AnswerRecords 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[AnswerRecordsTransport]] + _transport_registry["grpc"] = AnswerRecordsGrpcTransport + _transport_registry["grpc_asyncio"] = AnswerRecordsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[AnswerRecordsTransport]: + """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 AnswerRecordsClient(metaclass=AnswerRecordsClientMeta): + """Service for managing + [AnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecord]. + """ + + @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_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AnswerRecordsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + AnswerRecordsClient: 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) -> AnswerRecordsTransport: + """Return the transport used by the client instance. + + Returns: + AnswerRecordsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def answer_record_path(project: str, answer_record: str,) -> str: + """Return a fully-qualified answer_record string.""" + return "projects/{project}/answerRecords/{answer_record}".format( + project=project, answer_record=answer_record, + ) + + @staticmethod + def parse_answer_record_path(path: str) -> Dict[str, str]: + """Parse a answer_record path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/answerRecords/(?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, AnswerRecordsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the answer records 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, AnswerRecordsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, AnswerRecordsTransport): + # transport is a AnswerRecordsTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def get_answer_record( + self, + request: answer_record.GetAnswerRecordRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> answer_record.AnswerRecord: + r"""Deprecated. + Retrieves a specific answer record. + + Args: + request (google.cloud.dialogflow_v2beta1.types.GetAnswerRecordRequest): + The request object. Request message for + [AnswerRecords.GetAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.GetAnswerRecord]. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.AnswerRecord: + Answer records are records to manage answer history and feedbacks for + Dialogflow. + + Currently, answer record includes: + + - human agent assistant article suggestion + - human agent assistant faq article + + It doesn't include: + + - DetectIntent intent matching + - DetectIntent knowledge + + Answer records are not related to the conversation + history in the Dialogflow Console. A Record is + generated even when the end-user disables + conversation history in the console. Records are + created when there's a human agent assistant + suggestion generated. + + A typical workflow for customers provide feedback to + an answer is: + + 1. For human agent assistant, customers get + suggestion via ListSuggestions API. Together with + the answers, + [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] + are returned to the customers. + 2. The customer uses the + [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] + to call the [UpdateAnswerRecord][] method to send + feedback about a specific answer that they believe + is wrong. + + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a answer_record.GetAnswerRecordRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, answer_record.GetAnswerRecordRequest): + request = answer_record.GetAnswerRecordRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_answer_record] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def list_answer_records( + self, + request: answer_record.ListAnswerRecordsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListAnswerRecordsPager: + r"""Returns the list of all answer records in the + specified project in reverse chronological order. + + Args: + request (google.cloud.dialogflow_v2beta1.types.ListAnswerRecordsRequest): + The request object. Request message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecords.ListAnswerRecords]. + parent (str): + Required. The project to list all answer records for in + reverse chronological order. 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: + google.cloud.dialogflow_v2beta1.services.answer_records.pagers.ListAnswerRecordsPager: + Response message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecords.ListAnswerRecords]. + + 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 answer_record.ListAnswerRecordsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, answer_record.ListAnswerRecordsRequest): + request = answer_record.ListAnswerRecordsRequest(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_answer_records] + + # 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.ListAnswerRecordsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def update_answer_record( + self, + request: gcd_answer_record.UpdateAnswerRecordRequest = None, + *, + answer_record: gcd_answer_record.AnswerRecord = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_answer_record.AnswerRecord: + r"""Updates the specified answer record. + + Args: + request (google.cloud.dialogflow_v2beta1.types.UpdateAnswerRecordRequest): + The request object. Request message for + [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.UpdateAnswerRecord]. + answer_record (google.cloud.dialogflow_v2beta1.types.AnswerRecord): + Required. Answer record to update. + This corresponds to the ``answer_record`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.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: + google.cloud.dialogflow_v2beta1.types.AnswerRecord: + Answer records are records to manage answer history and feedbacks for + Dialogflow. + + Currently, answer record includes: + + - human agent assistant article suggestion + - human agent assistant faq article + + It doesn't include: + + - DetectIntent intent matching + - DetectIntent knowledge + + Answer records are not related to the conversation + history in the Dialogflow Console. A Record is + generated even when the end-user disables + conversation history in the console. Records are + created when there's a human agent assistant + suggestion generated. + + A typical workflow for customers provide feedback to + an answer is: + + 1. For human agent assistant, customers get + suggestion via ListSuggestions API. Together with + the answers, + [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] + are returned to the customers. + 2. The customer uses the + [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] + to call the [UpdateAnswerRecord][] method to send + feedback about a specific answer that they believe + is wrong. + + """ + # Create or coerce a 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([answer_record, 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 gcd_answer_record.UpdateAnswerRecordRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_answer_record.UpdateAnswerRecordRequest): + request = gcd_answer_record.UpdateAnswerRecordRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if answer_record is not None: + request.answer_record = answer_record + 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_answer_record] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("answer_record.name", request.answer_record.name),) + ), + ) + + # 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("AnswerRecordsClient",) diff --git a/google/cloud/dialogflow_v2beta1/services/answer_records/pagers.py b/google/cloud/dialogflow_v2beta1/services/answer_records/pagers.py new file mode 100644 index 000000000..1b0e3ba56 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/answer_records/pagers.py @@ -0,0 +1,157 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.dialogflow_v2beta1.types import answer_record + + +class ListAnswerRecordsPager: + """A pager for iterating through ``list_answer_records`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListAnswerRecordsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``answer_records`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListAnswerRecords`` requests and continue to iterate + through the ``answer_records`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListAnswerRecordsResponse` + 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[..., answer_record.ListAnswerRecordsResponse], + request: answer_record.ListAnswerRecordsRequest, + response: answer_record.ListAnswerRecordsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListAnswerRecordsRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListAnswerRecordsResponse): + 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 = answer_record.ListAnswerRecordsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[answer_record.ListAnswerRecordsResponse]: + 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[answer_record.AnswerRecord]: + for page in self.pages: + yield from page.answer_records + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListAnswerRecordsAsyncPager: + """A pager for iterating through ``list_answer_records`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListAnswerRecordsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``answer_records`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListAnswerRecords`` requests and continue to iterate + through the ``answer_records`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListAnswerRecordsResponse` + 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[answer_record.ListAnswerRecordsResponse]], + request: answer_record.ListAnswerRecordsRequest, + response: answer_record.ListAnswerRecordsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListAnswerRecordsRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListAnswerRecordsResponse): + 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 = answer_record.ListAnswerRecordsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[answer_record.ListAnswerRecordsResponse]: + 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[answer_record.AnswerRecord]: + async def async_generator(): + async for page in self.pages: + for response in page.answer_records: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflow_v2beta1/services/answer_records/transports/__init__.py b/google/cloud/dialogflow_v2beta1/services/answer_records/transports/__init__.py new file mode 100644 index 000000000..bbb65cf15 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/answer_records/transports/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import AnswerRecordsTransport +from .grpc import AnswerRecordsGrpcTransport +from .grpc_asyncio import AnswerRecordsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[AnswerRecordsTransport]] +_transport_registry["grpc"] = AnswerRecordsGrpcTransport +_transport_registry["grpc_asyncio"] = AnswerRecordsGrpcAsyncIOTransport + +__all__ = ( + "AnswerRecordsTransport", + "AnswerRecordsGrpcTransport", + "AnswerRecordsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflow_v2beta1/services/answer_records/transports/base.py b/google/cloud/dialogflow_v2beta1/services/answer_records/transports/base.py new file mode 100644 index 000000000..408c4495d --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/answer_records/transports/base.py @@ -0,0 +1,162 @@ +# -*- 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.dialogflow_v2beta1.types import answer_record +from google.cloud.dialogflow_v2beta1.types import answer_record as gcd_answer_record + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class AnswerRecordsTransport(abc.ABC): + """Abstract transport class for AnswerRecords.""" + + 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.get_answer_record: gapic_v1.method.wrap_method( + self.get_answer_record, default_timeout=None, client_info=client_info, + ), + self.list_answer_records: gapic_v1.method.wrap_method( + self.list_answer_records, default_timeout=None, client_info=client_info, + ), + self.update_answer_record: gapic_v1.method.wrap_method( + self.update_answer_record, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def get_answer_record( + self, + ) -> typing.Callable[ + [answer_record.GetAnswerRecordRequest], + typing.Union[ + answer_record.AnswerRecord, typing.Awaitable[answer_record.AnswerRecord] + ], + ]: + raise NotImplementedError() + + @property + def list_answer_records( + self, + ) -> typing.Callable[ + [answer_record.ListAnswerRecordsRequest], + typing.Union[ + answer_record.ListAnswerRecordsResponse, + typing.Awaitable[answer_record.ListAnswerRecordsResponse], + ], + ]: + raise NotImplementedError() + + @property + def update_answer_record( + self, + ) -> typing.Callable[ + [gcd_answer_record.UpdateAnswerRecordRequest], + typing.Union[ + gcd_answer_record.AnswerRecord, + typing.Awaitable[gcd_answer_record.AnswerRecord], + ], + ]: + raise NotImplementedError() + + +__all__ = ("AnswerRecordsTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/answer_records/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/answer_records/transports/grpc.py new file mode 100644 index 000000000..7a93b52ff --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/answer_records/transports/grpc.py @@ -0,0 +1,339 @@ +# -*- 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.dialogflow_v2beta1.types import answer_record +from google.cloud.dialogflow_v2beta1.types import answer_record as gcd_answer_record + +from .base import AnswerRecordsTransport, DEFAULT_CLIENT_INFO + + +class AnswerRecordsGrpcTransport(AnswerRecordsTransport): + """gRPC backend transport for AnswerRecords. + + Service for managing + [AnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecord]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + self._stubs = {} # type: Dict[str, Callable] + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + @classmethod + def create_channel( + cls, + host: str = "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 (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def get_answer_record( + self, + ) -> Callable[[answer_record.GetAnswerRecordRequest], answer_record.AnswerRecord]: + r"""Return a callable for the get answer record method over gRPC. + + Deprecated. + Retrieves a specific answer record. + + Returns: + Callable[[~.GetAnswerRecordRequest], + ~.AnswerRecord]: + 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_answer_record" not in self._stubs: + self._stubs["get_answer_record"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.AnswerRecords/GetAnswerRecord", + request_serializer=answer_record.GetAnswerRecordRequest.serialize, + response_deserializer=answer_record.AnswerRecord.deserialize, + ) + return self._stubs["get_answer_record"] + + @property + def list_answer_records( + self, + ) -> Callable[ + [answer_record.ListAnswerRecordsRequest], + answer_record.ListAnswerRecordsResponse, + ]: + r"""Return a callable for the list answer records method over gRPC. + + Returns the list of all answer records in the + specified project in reverse chronological order. + + Returns: + Callable[[~.ListAnswerRecordsRequest], + ~.ListAnswerRecordsResponse]: + 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_answer_records" not in self._stubs: + self._stubs["list_answer_records"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.AnswerRecords/ListAnswerRecords", + request_serializer=answer_record.ListAnswerRecordsRequest.serialize, + response_deserializer=answer_record.ListAnswerRecordsResponse.deserialize, + ) + return self._stubs["list_answer_records"] + + @property + def update_answer_record( + self, + ) -> Callable[ + [gcd_answer_record.UpdateAnswerRecordRequest], gcd_answer_record.AnswerRecord + ]: + r"""Return a callable for the update answer record method over gRPC. + + Updates the specified answer record. + + Returns: + Callable[[~.UpdateAnswerRecordRequest], + ~.AnswerRecord]: + 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_answer_record" not in self._stubs: + self._stubs["update_answer_record"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.AnswerRecords/UpdateAnswerRecord", + request_serializer=gcd_answer_record.UpdateAnswerRecordRequest.serialize, + response_deserializer=gcd_answer_record.AnswerRecord.deserialize, + ) + return self._stubs["update_answer_record"] + + +__all__ = ("AnswerRecordsGrpcTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/answer_records/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/answer_records/transports/grpc_asyncio.py new file mode 100644 index 000000000..62c4531f8 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/answer_records/transports/grpc_asyncio.py @@ -0,0 +1,346 @@ +# -*- 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.dialogflow_v2beta1.types import answer_record +from google.cloud.dialogflow_v2beta1.types import answer_record as gcd_answer_record + +from .base import AnswerRecordsTransport, DEFAULT_CLIENT_INFO +from .grpc import AnswerRecordsGrpcTransport + + +class AnswerRecordsGrpcAsyncIOTransport(AnswerRecordsTransport): + """gRPC AsyncIO backend transport for AnswerRecords. + + Service for managing + [AnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecord]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + self._stubs = {} + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def get_answer_record( + self, + ) -> Callable[ + [answer_record.GetAnswerRecordRequest], Awaitable[answer_record.AnswerRecord] + ]: + r"""Return a callable for the get answer record method over gRPC. + + Deprecated. + Retrieves a specific answer record. + + Returns: + Callable[[~.GetAnswerRecordRequest], + Awaitable[~.AnswerRecord]]: + 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_answer_record" not in self._stubs: + self._stubs["get_answer_record"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.AnswerRecords/GetAnswerRecord", + request_serializer=answer_record.GetAnswerRecordRequest.serialize, + response_deserializer=answer_record.AnswerRecord.deserialize, + ) + return self._stubs["get_answer_record"] + + @property + def list_answer_records( + self, + ) -> Callable[ + [answer_record.ListAnswerRecordsRequest], + Awaitable[answer_record.ListAnswerRecordsResponse], + ]: + r"""Return a callable for the list answer records method over gRPC. + + Returns the list of all answer records in the + specified project in reverse chronological order. + + Returns: + Callable[[~.ListAnswerRecordsRequest], + Awaitable[~.ListAnswerRecordsResponse]]: + 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_answer_records" not in self._stubs: + self._stubs["list_answer_records"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.AnswerRecords/ListAnswerRecords", + request_serializer=answer_record.ListAnswerRecordsRequest.serialize, + response_deserializer=answer_record.ListAnswerRecordsResponse.deserialize, + ) + return self._stubs["list_answer_records"] + + @property + def update_answer_record( + self, + ) -> Callable[ + [gcd_answer_record.UpdateAnswerRecordRequest], + Awaitable[gcd_answer_record.AnswerRecord], + ]: + r"""Return a callable for the update answer record method over gRPC. + + Updates the specified answer record. + + Returns: + Callable[[~.UpdateAnswerRecordRequest], + Awaitable[~.AnswerRecord]]: + 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_answer_record" not in self._stubs: + self._stubs["update_answer_record"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.AnswerRecords/UpdateAnswerRecord", + request_serializer=gcd_answer_record.UpdateAnswerRecordRequest.serialize, + response_deserializer=gcd_answer_record.AnswerRecord.deserialize, + ) + return self._stubs["update_answer_record"] + + +__all__ = ("AnswerRecordsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/contexts/async_client.py b/google/cloud/dialogflow_v2beta1/services/contexts/async_client.py index 1bb413794..5aceb21ee 100644 --- a/google/cloud/dialogflow_v2beta1/services/contexts/async_client.py +++ b/google/cloud/dialogflow_v2beta1/services/contexts/async_client.py @@ -73,7 +73,36 @@ class ContextsAsyncClient: common_location_path = staticmethod(ContextsClient.common_location_path) parse_common_location_path = staticmethod(ContextsClient.parse_common_location_path) - from_service_account_file = ContextsClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ContextsAsyncClient: The constructed client. + """ + return ContextsClient.from_service_account_info.__func__(ContextsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ContextsAsyncClient: The constructed client. + """ + return ContextsClient.from_service_account_file.__func__(ContextsAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -150,7 +179,7 @@ async def list_contexts( session. Args: - request (:class:`~.context.ListContextsRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.ListContextsRequest`): The request object. The request message for [Contexts.ListContexts][google.cloud.dialogflow.v2beta1.Contexts.ListContexts]. parent (:class:`str`): @@ -166,6 +195,7 @@ async def list_contexts( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -177,7 +207,7 @@ async def list_contexts( sent along with the request as metadata. Returns: - ~.pagers.ListContextsAsyncPager: + google.cloud.dialogflow_v2beta1.services.contexts.pagers.ListContextsAsyncPager: The response message for [Contexts.ListContexts][google.cloud.dialogflow.v2beta1.Contexts.ListContexts]. @@ -241,7 +271,7 @@ async def get_context( r"""Retrieves the specified context. Args: - request (:class:`~.context.GetContextRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.GetContextRequest`): The request object. The request message for [Contexts.GetContext][google.cloud.dialogflow.v2beta1.Contexts.GetContext]. name (:class:`str`): @@ -256,6 +286,7 @@ async def get_context( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -267,26 +298,26 @@ async def get_context( sent along with the request as metadata. Returns: - ~.context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2beta1.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -342,7 +373,7 @@ async def create_context( context. Args: - request (:class:`~.gcd_context.CreateContextRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.CreateContextRequest`): The request object. The request message for [Contexts.CreateContext][google.cloud.dialogflow.v2beta1.Contexts.CreateContext]. parent (:class:`str`): @@ -358,10 +389,11 @@ async def create_context( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - context (:class:`~.gcd_context.Context`): + context (:class:`google.cloud.dialogflow_v2beta1.types.Context`): Required. The context to create. This corresponds to the ``context`` field on the ``request`` instance; if ``request`` is provided, this @@ -374,26 +406,26 @@ async def create_context( sent along with the request as metadata. Returns: - ~.gcd_context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2beta1.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -449,17 +481,18 @@ async def update_context( r"""Updates the specified context. Args: - request (:class:`~.gcd_context.UpdateContextRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.UpdateContextRequest`): The request object. The request message for [Contexts.UpdateContext][google.cloud.dialogflow.v2beta1.Contexts.UpdateContext]. - context (:class:`~.gcd_context.Context`): + context (:class:`google.cloud.dialogflow_v2beta1.types.Context`): Required. The context to update. This corresponds to the ``context`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): Optional. 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. @@ -471,26 +504,26 @@ async def update_context( sent along with the request as metadata. Returns: - ~.gcd_context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2beta1.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -547,7 +580,7 @@ async def delete_context( r"""Deletes the specified context. Args: - request (:class:`~.context.DeleteContextRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.DeleteContextRequest`): The request object. The request message for [Contexts.DeleteContext][google.cloud.dialogflow.v2beta1.Contexts.DeleteContext]. name (:class:`str`): @@ -563,6 +596,7 @@ async def delete_context( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -622,7 +656,7 @@ async def delete_all_contexts( r"""Deletes all active contexts in the specified session. Args: - request (:class:`~.context.DeleteAllContextsRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.DeleteAllContextsRequest`): The request object. The request message for [Contexts.DeleteAllContexts][google.cloud.dialogflow.v2beta1.Contexts.DeleteAllContexts]. parent (:class:`str`): @@ -638,6 +672,7 @@ async def delete_all_contexts( 'us' location. If ``Environment ID`` is not specified we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. diff --git a/google/cloud/dialogflow_v2beta1/services/contexts/client.py b/google/cloud/dialogflow_v2beta1/services/contexts/client.py index faa8fe6b5..08291572e 100644 --- a/google/cloud/dialogflow_v2beta1/services/contexts/client.py +++ b/google/cloud/dialogflow_v2beta1/services/contexts/client.py @@ -113,6 +113,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ContextsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -125,7 +141,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + ContextsClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -233,10 +249,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.ContextsTransport]): The + transport (Union[str, ContextsTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (client_options_lib.ClientOptions): Custom options for the + client_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -272,21 +288,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -329,7 +341,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -347,10 +359,10 @@ def list_contexts( session. Args: - request (:class:`~.context.ListContextsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListContextsRequest): The request object. The request message for [Contexts.ListContexts][google.cloud.dialogflow.v2beta1.Contexts.ListContexts]. - parent (:class:`str`): + parent (str): Required. The session to list all contexts from. Supported formats: @@ -363,6 +375,7 @@ def list_contexts( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -374,7 +387,7 @@ def list_contexts( sent along with the request as metadata. Returns: - ~.pagers.ListContextsPager: + google.cloud.dialogflow_v2beta1.services.contexts.pagers.ListContextsPager: The response message for [Contexts.ListContexts][google.cloud.dialogflow.v2beta1.Contexts.ListContexts]. @@ -439,10 +452,10 @@ def get_context( r"""Retrieves the specified context. Args: - request (:class:`~.context.GetContextRequest`): + request (google.cloud.dialogflow_v2beta1.types.GetContextRequest): The request object. The request message for [Contexts.GetContext][google.cloud.dialogflow.v2beta1.Contexts.GetContext]. - name (:class:`str`): + name (str): Required. The name of the context. Supported formats: - ``projects//agent/sessions//contexts/``, @@ -454,6 +467,7 @@ def get_context( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -465,26 +479,26 @@ def get_context( sent along with the request as metadata. Returns: - ~.context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2beta1.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -541,10 +555,10 @@ def create_context( context. Args: - request (:class:`~.gcd_context.CreateContextRequest`): + request (google.cloud.dialogflow_v2beta1.types.CreateContextRequest): The request object. The request message for [Contexts.CreateContext][google.cloud.dialogflow.v2beta1.Contexts.CreateContext]. - parent (:class:`str`): + parent (str): Required. The session to create a context for. Supported formats: @@ -557,10 +571,11 @@ def create_context( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - context (:class:`~.gcd_context.Context`): + context (google.cloud.dialogflow_v2beta1.types.Context): Required. The context to create. This corresponds to the ``context`` field on the ``request`` instance; if ``request`` is provided, this @@ -573,26 +588,26 @@ def create_context( sent along with the request as metadata. Returns: - ~.gcd_context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2beta1.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -649,17 +664,18 @@ def update_context( r"""Updates the specified context. Args: - request (:class:`~.gcd_context.UpdateContextRequest`): + request (google.cloud.dialogflow_v2beta1.types.UpdateContextRequest): The request object. The request message for [Contexts.UpdateContext][google.cloud.dialogflow.v2beta1.Contexts.UpdateContext]. - context (:class:`~.gcd_context.Context`): + context (google.cloud.dialogflow_v2beta1.types.Context): Required. The context to update. This corresponds to the ``context`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. 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. @@ -671,26 +687,26 @@ def update_context( sent along with the request as metadata. Returns: - ~.gcd_context.Context: - Dialogflow contexts are similar to natural language - context. If a person says to you "they are orange", you - need context in order to understand what "they" is - referring to. Similarly, for Dialogflow to handle an - end-user expression like that, it needs to be provided - with context in order to correctly match an intent. - - Using contexts, you can control the flow of a - conversation. You can configure contexts for an intent - by setting input and output contexts, which are - identified by string names. When an intent is matched, - any configured output contexts for that intent become - active. While any contexts are active, Dialogflow is - more likely to match intents that are configured with - input contexts that correspond to the currently active - contexts. - - For more information about context, see the `Contexts - guide `__. + google.cloud.dialogflow_v2beta1.types.Context: + Dialogflow contexts are similar to natural language context. If a person says + to you "they are orange", you need context in order + to understand what "they" is referring to. Similarly, + for Dialogflow to handle an end-user expression like + that, it needs to be provided with context in order + to correctly match an intent. + + Using contexts, you can control the flow of a + conversation. You can configure contexts for an + intent by setting input and output contexts, which + are identified by string names. When an intent is + matched, any configured output contexts for that + intent become active. While any contexts are active, + Dialogflow is more likely to match intents that are + configured with input contexts that correspond to the + currently active contexts. + + For more information about context, see the [Contexts + guide](\ https://cloud.google.com/dialogflow/docs/contexts-overview). """ # Create or coerce a protobuf request object. @@ -748,10 +764,10 @@ def delete_context( r"""Deletes the specified context. Args: - request (:class:`~.context.DeleteContextRequest`): + request (google.cloud.dialogflow_v2beta1.types.DeleteContextRequest): The request object. The request message for [Contexts.DeleteContext][google.cloud.dialogflow.v2beta1.Contexts.DeleteContext]. - name (:class:`str`): + name (str): Required. The name of the context to delete. Supported formats: @@ -764,6 +780,7 @@ def delete_context( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -824,10 +841,10 @@ def delete_all_contexts( r"""Deletes all active contexts in the specified session. Args: - request (:class:`~.context.DeleteAllContextsRequest`): + request (google.cloud.dialogflow_v2beta1.types.DeleteAllContextsRequest): The request object. The request message for [Contexts.DeleteAllContexts][google.cloud.dialogflow.v2beta1.Contexts.DeleteAllContexts]. - parent (:class:`str`): + parent (str): Required. The name of the session to delete all contexts from. Supported formats: @@ -840,6 +857,7 @@ def delete_all_contexts( 'us' location. If ``Environment ID`` is not specified we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. diff --git a/google/cloud/dialogflow_v2beta1/services/contexts/pagers.py b/google/cloud/dialogflow_v2beta1/services/contexts/pagers.py index 84c0ae735..9460cfd11 100644 --- a/google/cloud/dialogflow_v2beta1/services/contexts/pagers.py +++ b/google/cloud/dialogflow_v2beta1/services/contexts/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2beta1.types import context @@ -24,7 +33,7 @@ class ListContextsPager: """A pager for iterating through ``list_contexts`` requests. This class thinly wraps an initial - :class:`~.context.ListContextsResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListContextsResponse` object, and provides an ``__iter__`` method to iterate through its ``contexts`` field. @@ -33,7 +42,7 @@ class ListContextsPager: through the ``contexts`` field on the corresponding responses. - All the usual :class:`~.context.ListContextsResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListContextsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.context.ListContextsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListContextsRequest): The initial request object. - response (:class:`~.context.ListContextsResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListContextsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListContextsAsyncPager: """A pager for iterating through ``list_contexts`` requests. This class thinly wraps an initial - :class:`~.context.ListContextsResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListContextsResponse` object, and provides an ``__aiter__`` method to iterate through its ``contexts`` field. @@ -95,7 +104,7 @@ class ListContextsAsyncPager: through the ``contexts`` field on the corresponding responses. - All the usual :class:`~.context.ListContextsResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListContextsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.context.ListContextsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListContextsRequest): The initial request object. - response (:class:`~.context.ListContextsResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListContextsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2beta1/services/contexts/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/contexts/transports/grpc.py index 5e0906ee0..020b3d9ec 100644 --- a/google/cloud/dialogflow_v2beta1/services/contexts/transports/grpc.py +++ b/google/cloud/dialogflow_v2beta1/services/contexts/transports/grpc.py @@ -60,6 +60,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -90,6 +91,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -106,6 +111,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -115,11 +125,6 @@ def __init__( 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 @@ -163,12 +168,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/contexts/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/contexts/transports/grpc_asyncio.py index 005d57e6d..f06a162f5 100644 --- a/google/cloud/dialogflow_v2beta1/services/contexts/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2beta1/services/contexts/transports/grpc_asyncio.py @@ -104,6 +104,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -135,6 +136,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -151,6 +156,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -160,11 +170,6 @@ def __init__( 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 @@ -208,12 +213,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/conversation_profiles/__init__.py b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/__init__.py new file mode 100644 index 000000000..9ab8de0b8 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/__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 ConversationProfilesClient +from .async_client import ConversationProfilesAsyncClient + +__all__ = ( + "ConversationProfilesClient", + "ConversationProfilesAsyncClient", +) diff --git a/google/cloud/dialogflow_v2beta1/services/conversation_profiles/async_client.py b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/async_client.py new file mode 100644 index 000000000..a052858ff --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/async_client.py @@ -0,0 +1,612 @@ +# -*- 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.dialogflow_v2beta1.services.conversation_profiles import pagers +from google.cloud.dialogflow_v2beta1.types import audio_config +from google.cloud.dialogflow_v2beta1.types import conversation_profile +from google.cloud.dialogflow_v2beta1.types import ( + conversation_profile as gcd_conversation_profile, +) +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import ConversationProfilesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import ConversationProfilesGrpcAsyncIOTransport +from .client import ConversationProfilesClient + + +class ConversationProfilesAsyncClient: + """Service for managing + [ConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfile]. + """ + + _client: ConversationProfilesClient + + DEFAULT_ENDPOINT = ConversationProfilesClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = ConversationProfilesClient.DEFAULT_MTLS_ENDPOINT + + agent_path = staticmethod(ConversationProfilesClient.agent_path) + parse_agent_path = staticmethod(ConversationProfilesClient.parse_agent_path) + conversation_profile_path = staticmethod( + ConversationProfilesClient.conversation_profile_path + ) + parse_conversation_profile_path = staticmethod( + ConversationProfilesClient.parse_conversation_profile_path + ) + document_path = staticmethod(ConversationProfilesClient.document_path) + parse_document_path = staticmethod(ConversationProfilesClient.parse_document_path) + knowledge_base_path = staticmethod(ConversationProfilesClient.knowledge_base_path) + parse_knowledge_base_path = staticmethod( + ConversationProfilesClient.parse_knowledge_base_path + ) + + common_billing_account_path = staticmethod( + ConversationProfilesClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + ConversationProfilesClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(ConversationProfilesClient.common_folder_path) + parse_common_folder_path = staticmethod( + ConversationProfilesClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + ConversationProfilesClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + ConversationProfilesClient.parse_common_organization_path + ) + + common_project_path = staticmethod(ConversationProfilesClient.common_project_path) + parse_common_project_path = staticmethod( + ConversationProfilesClient.parse_common_project_path + ) + + common_location_path = staticmethod(ConversationProfilesClient.common_location_path) + parse_common_location_path = staticmethod( + ConversationProfilesClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationProfilesAsyncClient: The constructed client. + """ + return ConversationProfilesClient.from_service_account_info.__func__(ConversationProfilesAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationProfilesAsyncClient: The constructed client. + """ + return ConversationProfilesClient.from_service_account_file.__func__(ConversationProfilesAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> ConversationProfilesTransport: + """Return the transport used by the client instance. + + Returns: + ConversationProfilesTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(ConversationProfilesClient).get_transport_class, + type(ConversationProfilesClient), + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, ConversationProfilesTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the conversation profiles 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, ~.ConversationProfilesTransport]): 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 = ConversationProfilesClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def list_conversation_profiles( + self, + request: conversation_profile.ListConversationProfilesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListConversationProfilesAsyncPager: + r"""Returns the list of all conversation profiles in the + specified project. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.ListConversationProfilesRequest`): + The request object. The request message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfiles.ListConversationProfiles]. + parent (:class:`str`): + Required. The project to list all conversation profiles + from. 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: + google.cloud.dialogflow_v2beta1.services.conversation_profiles.pagers.ListConversationProfilesAsyncPager: + The response message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfiles.ListConversationProfiles]. + + 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 = conversation_profile.ListConversationProfilesRequest(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_conversation_profiles, + 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.ListConversationProfilesAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_conversation_profile( + self, + request: conversation_profile.GetConversationProfileRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation_profile.ConversationProfile: + r"""Retrieves the specified conversation profile. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.GetConversationProfileRequest`): + The request object. The request message for + [ConversationProfiles.GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile]. + name (:class:`str`): + Required. The resource name of the conversation profile. + Format: + ``projects//locations//conversationProfiles/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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 = conversation_profile.GetConversationProfileRequest(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_conversation_profile, + 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_conversation_profile( + self, + request: gcd_conversation_profile.CreateConversationProfileRequest = None, + *, + parent: str = None, + conversation_profile: gcd_conversation_profile.ConversationProfile = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation_profile.ConversationProfile: + r"""Creates a conversation profile in the specified project. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile] + API. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.CreateConversationProfileRequest`): + The request object. The request message for + [ConversationProfiles.CreateConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.CreateConversationProfile]. + parent (:class:`str`): + Required. The project to create a conversation profile + for. Format: + ``projects//locations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + conversation_profile (:class:`google.cloud.dialogflow_v2beta1.types.ConversationProfile`): + Required. The conversation profile to + create. + + This corresponds to the ``conversation_profile`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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, conversation_profile]) + if request is 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 = gcd_conversation_profile.CreateConversationProfileRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if conversation_profile is not None: + request.conversation_profile = conversation_profile + + # 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_conversation_profile, + 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_conversation_profile( + self, + request: gcd_conversation_profile.UpdateConversationProfileRequest = None, + *, + conversation_profile: gcd_conversation_profile.ConversationProfile = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation_profile.ConversationProfile: + r"""Updates the specified conversation profile. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile] + API. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.UpdateConversationProfileRequest`): + The request object. The request message for + [ConversationProfiles.UpdateConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.UpdateConversationProfile]. + conversation_profile (:class:`google.cloud.dialogflow_v2beta1.types.ConversationProfile`): + Required. The conversation profile to + update. + + This corresponds to the ``conversation_profile`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): + Required. The mask to control which + fields to update. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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([conversation_profile, 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 = gcd_conversation_profile.UpdateConversationProfileRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if conversation_profile is not None: + request.conversation_profile = conversation_profile + 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_conversation_profile, + 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( + (("conversation_profile.name", request.conversation_profile.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def delete_conversation_profile( + self, + request: conversation_profile.DeleteConversationProfileRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified conversation profile. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.DeleteConversationProfileRequest`): + The request object. The request message for + [ConversationProfiles.DeleteConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.DeleteConversationProfile]. + This operation fails if the conversation profile is + still referenced from a phone number. + name (:class:`str`): + Required. The name of the conversation profile to + delete. Format: + ``projects//locations//conversationProfiles/``. + + 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 = conversation_profile.DeleteConversationProfileRequest(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_conversation_profile, + 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ConversationProfilesAsyncClient",) diff --git a/google/cloud/dialogflow_v2beta1/services/conversation_profiles/client.py b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/client.py new file mode 100644 index 000000000..3e4c17399 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/client.py @@ -0,0 +1,838 @@ +# -*- 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.dialogflow_v2beta1.services.conversation_profiles import pagers +from google.cloud.dialogflow_v2beta1.types import audio_config +from google.cloud.dialogflow_v2beta1.types import conversation_profile +from google.cloud.dialogflow_v2beta1.types import ( + conversation_profile as gcd_conversation_profile, +) +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import ConversationProfilesTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import ConversationProfilesGrpcTransport +from .transports.grpc_asyncio import ConversationProfilesGrpcAsyncIOTransport + + +class ConversationProfilesClientMeta(type): + """Metaclass for the ConversationProfiles 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[ConversationProfilesTransport]] + _transport_registry["grpc"] = ConversationProfilesGrpcTransport + _transport_registry["grpc_asyncio"] = ConversationProfilesGrpcAsyncIOTransport + + def get_transport_class( + cls, label: str = None, + ) -> Type[ConversationProfilesTransport]: + """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 ConversationProfilesClient(metaclass=ConversationProfilesClientMeta): + """Service for managing + [ConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfile]. + """ + + @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_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationProfilesClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationProfilesClient: 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) -> ConversationProfilesTransport: + """Return the transport used by the client instance. + + Returns: + ConversationProfilesTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def agent_path(project: str,) -> str: + """Return a fully-qualified agent string.""" + return "projects/{project}/agent".format(project=project,) + + @staticmethod + def parse_agent_path(path: str) -> Dict[str, str]: + """Parse a agent path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/agent$", path) + return m.groupdict() if m else {} + + @staticmethod + def conversation_profile_path(project: str, conversation_profile: str,) -> str: + """Return a fully-qualified conversation_profile string.""" + return "projects/{project}/conversationProfiles/{conversation_profile}".format( + project=project, conversation_profile=conversation_profile, + ) + + @staticmethod + def parse_conversation_profile_path(path: str) -> Dict[str, str]: + """Parse a conversation_profile path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversationProfiles/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def document_path(project: str, knowledge_base: str, document: str,) -> str: + """Return a fully-qualified document string.""" + return "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, knowledge_base=knowledge_base, document=document, + ) + + @staticmethod + def parse_document_path(path: str) -> Dict[str, str]: + """Parse a document path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/knowledgeBases/(?P.+?)/documents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def knowledge_base_path(project: str, knowledge_base: str,) -> str: + """Return a fully-qualified knowledge_base string.""" + return "projects/{project}/knowledgeBases/{knowledge_base}".format( + project=project, knowledge_base=knowledge_base, + ) + + @staticmethod + def parse_knowledge_base_path(path: str) -> Dict[str, str]: + """Parse a knowledge_base path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/knowledgeBases/(?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, ConversationProfilesTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the conversation profiles 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, ConversationProfilesTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, ConversationProfilesTransport): + # transport is a ConversationProfilesTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def list_conversation_profiles( + self, + request: conversation_profile.ListConversationProfilesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListConversationProfilesPager: + r"""Returns the list of all conversation profiles in the + specified project. + + Args: + request (google.cloud.dialogflow_v2beta1.types.ListConversationProfilesRequest): + The request object. The request message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfiles.ListConversationProfiles]. + parent (str): + Required. The project to list all conversation profiles + from. 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: + google.cloud.dialogflow_v2beta1.services.conversation_profiles.pagers.ListConversationProfilesPager: + The response message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfiles.ListConversationProfiles]. + + 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 conversation_profile.ListConversationProfilesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, conversation_profile.ListConversationProfilesRequest + ): + request = conversation_profile.ListConversationProfilesRequest(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_conversation_profiles + ] + + # 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.ListConversationProfilesPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_conversation_profile( + self, + request: conversation_profile.GetConversationProfileRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation_profile.ConversationProfile: + r"""Retrieves the specified conversation profile. + + Args: + request (google.cloud.dialogflow_v2beta1.types.GetConversationProfileRequest): + The request object. The request message for + [ConversationProfiles.GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile]. + name (str): + Required. The resource name of the conversation profile. + Format: + ``projects//locations//conversationProfiles/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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 conversation_profile.GetConversationProfileRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation_profile.GetConversationProfileRequest): + request = conversation_profile.GetConversationProfileRequest(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_conversation_profile] + + # 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_conversation_profile( + self, + request: gcd_conversation_profile.CreateConversationProfileRequest = None, + *, + parent: str = None, + conversation_profile: gcd_conversation_profile.ConversationProfile = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation_profile.ConversationProfile: + r"""Creates a conversation profile in the specified project. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile] + API. + + Args: + request (google.cloud.dialogflow_v2beta1.types.CreateConversationProfileRequest): + The request object. The request message for + [ConversationProfiles.CreateConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.CreateConversationProfile]. + parent (str): + Required. The project to create a conversation profile + for. Format: + ``projects//locations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + conversation_profile (google.cloud.dialogflow_v2beta1.types.ConversationProfile): + Required. The conversation profile to + create. + + This corresponds to the ``conversation_profile`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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, conversation_profile]) + if request is 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 gcd_conversation_profile.CreateConversationProfileRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, gcd_conversation_profile.CreateConversationProfileRequest + ): + request = gcd_conversation_profile.CreateConversationProfileRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if conversation_profile is not None: + request.conversation_profile = conversation_profile + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[ + self._transport.create_conversation_profile + ] + + # 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_conversation_profile( + self, + request: gcd_conversation_profile.UpdateConversationProfileRequest = None, + *, + conversation_profile: gcd_conversation_profile.ConversationProfile = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation_profile.ConversationProfile: + r"""Updates the specified conversation profile. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile] + API. + + Args: + request (google.cloud.dialogflow_v2beta1.types.UpdateConversationProfileRequest): + The request object. The request message for + [ConversationProfiles.UpdateConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.UpdateConversationProfile]. + conversation_profile (google.cloud.dialogflow_v2beta1.types.ConversationProfile): + Required. The conversation profile to + update. + + This corresponds to the ``conversation_profile`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. The mask to control which + fields to update. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.ConversationProfile: + Defines the services to connect to + incoming Dialogflow conversations. + + """ + # Create or coerce a 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([conversation_profile, 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 gcd_conversation_profile.UpdateConversationProfileRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, gcd_conversation_profile.UpdateConversationProfileRequest + ): + request = gcd_conversation_profile.UpdateConversationProfileRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if conversation_profile is not None: + request.conversation_profile = conversation_profile + 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_conversation_profile + ] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("conversation_profile.name", request.conversation_profile.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def delete_conversation_profile( + self, + request: conversation_profile.DeleteConversationProfileRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Deletes the specified conversation profile. + + Args: + request (google.cloud.dialogflow_v2beta1.types.DeleteConversationProfileRequest): + The request object. The request message for + [ConversationProfiles.DeleteConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.DeleteConversationProfile]. + This operation fails if the conversation profile is + still referenced from a phone number. + name (str): + Required. The name of the conversation profile to + delete. Format: + ``projects//locations//conversationProfiles/``. + + 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 conversation_profile.DeleteConversationProfileRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance( + request, conversation_profile.DeleteConversationProfileRequest + ): + request = conversation_profile.DeleteConversationProfileRequest(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_conversation_profile + ] + + # 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ConversationProfilesClient",) diff --git a/google/cloud/dialogflow_v2beta1/services/conversation_profiles/pagers.py b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/pagers.py new file mode 100644 index 000000000..1e03482d0 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/pagers.py @@ -0,0 +1,161 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.dialogflow_v2beta1.types import conversation_profile + + +class ListConversationProfilesPager: + """A pager for iterating through ``list_conversation_profiles`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListConversationProfilesResponse` object, and + provides an ``__iter__`` method to iterate through its + ``conversation_profiles`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListConversationProfiles`` requests and continue to iterate + through the ``conversation_profiles`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListConversationProfilesResponse` + 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[..., conversation_profile.ListConversationProfilesResponse], + request: conversation_profile.ListConversationProfilesRequest, + response: conversation_profile.ListConversationProfilesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListConversationProfilesRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListConversationProfilesResponse): + 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 = conversation_profile.ListConversationProfilesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[conversation_profile.ListConversationProfilesResponse]: + 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[conversation_profile.ConversationProfile]: + for page in self.pages: + yield from page.conversation_profiles + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListConversationProfilesAsyncPager: + """A pager for iterating through ``list_conversation_profiles`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListConversationProfilesResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``conversation_profiles`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListConversationProfiles`` requests and continue to iterate + through the ``conversation_profiles`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListConversationProfilesResponse` + 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[conversation_profile.ListConversationProfilesResponse] + ], + request: conversation_profile.ListConversationProfilesRequest, + response: conversation_profile.ListConversationProfilesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListConversationProfilesRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListConversationProfilesResponse): + 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 = conversation_profile.ListConversationProfilesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages( + self, + ) -> AsyncIterable[conversation_profile.ListConversationProfilesResponse]: + 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[conversation_profile.ConversationProfile]: + async def async_generator(): + async for page in self.pages: + for response in page.conversation_profiles: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflow_v2beta1/services/conversation_profiles/transports/__init__.py b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/transports/__init__.py new file mode 100644 index 000000000..e97774bcf --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/transports/__init__.py @@ -0,0 +1,37 @@ +# -*- 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 ConversationProfilesTransport +from .grpc import ConversationProfilesGrpcTransport +from .grpc_asyncio import ConversationProfilesGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = ( + OrderedDict() +) # type: Dict[str, Type[ConversationProfilesTransport]] +_transport_registry["grpc"] = ConversationProfilesGrpcTransport +_transport_registry["grpc_asyncio"] = ConversationProfilesGrpcAsyncIOTransport + +__all__ = ( + "ConversationProfilesTransport", + "ConversationProfilesGrpcTransport", + "ConversationProfilesGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflow_v2beta1/services/conversation_profiles/transports/base.py b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/transports/base.py new file mode 100644 index 000000000..4bc3e09fd --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/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.dialogflow_v2beta1.types import conversation_profile +from google.cloud.dialogflow_v2beta1.types import ( + conversation_profile as gcd_conversation_profile, +) +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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class ConversationProfilesTransport(abc.ABC): + """Abstract transport class for ConversationProfiles.""" + + 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_conversation_profiles: gapic_v1.method.wrap_method( + self.list_conversation_profiles, + default_timeout=None, + client_info=client_info, + ), + self.get_conversation_profile: gapic_v1.method.wrap_method( + self.get_conversation_profile, + default_timeout=None, + client_info=client_info, + ), + self.create_conversation_profile: gapic_v1.method.wrap_method( + self.create_conversation_profile, + default_timeout=None, + client_info=client_info, + ), + self.update_conversation_profile: gapic_v1.method.wrap_method( + self.update_conversation_profile, + default_timeout=None, + client_info=client_info, + ), + self.delete_conversation_profile: gapic_v1.method.wrap_method( + self.delete_conversation_profile, + default_timeout=None, + client_info=client_info, + ), + } + + @property + def list_conversation_profiles( + self, + ) -> typing.Callable[ + [conversation_profile.ListConversationProfilesRequest], + typing.Union[ + conversation_profile.ListConversationProfilesResponse, + typing.Awaitable[conversation_profile.ListConversationProfilesResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_conversation_profile( + self, + ) -> typing.Callable[ + [conversation_profile.GetConversationProfileRequest], + typing.Union[ + conversation_profile.ConversationProfile, + typing.Awaitable[conversation_profile.ConversationProfile], + ], + ]: + raise NotImplementedError() + + @property + def create_conversation_profile( + self, + ) -> typing.Callable[ + [gcd_conversation_profile.CreateConversationProfileRequest], + typing.Union[ + gcd_conversation_profile.ConversationProfile, + typing.Awaitable[gcd_conversation_profile.ConversationProfile], + ], + ]: + raise NotImplementedError() + + @property + def update_conversation_profile( + self, + ) -> typing.Callable[ + [gcd_conversation_profile.UpdateConversationProfileRequest], + typing.Union[ + gcd_conversation_profile.ConversationProfile, + typing.Awaitable[gcd_conversation_profile.ConversationProfile], + ], + ]: + raise NotImplementedError() + + @property + def delete_conversation_profile( + self, + ) -> typing.Callable[ + [conversation_profile.DeleteConversationProfileRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + +__all__ = ("ConversationProfilesTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/conversation_profiles/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/transports/grpc.py new file mode 100644 index 000000000..3d5442260 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/transports/grpc.py @@ -0,0 +1,412 @@ +# -*- 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.dialogflow_v2beta1.types import conversation_profile +from google.cloud.dialogflow_v2beta1.types import ( + conversation_profile as gcd_conversation_profile, +) +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import ConversationProfilesTransport, DEFAULT_CLIENT_INFO + + +class ConversationProfilesGrpcTransport(ConversationProfilesTransport): + """gRPC backend transport for ConversationProfiles. + + Service for managing + [ConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfile]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + self._stubs = {} # type: Dict[str, Callable] + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + @classmethod + def create_channel( + cls, + host: str = "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 (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def list_conversation_profiles( + self, + ) -> Callable[ + [conversation_profile.ListConversationProfilesRequest], + conversation_profile.ListConversationProfilesResponse, + ]: + r"""Return a callable for the list conversation profiles method over gRPC. + + Returns the list of all conversation profiles in the + specified project. + + Returns: + Callable[[~.ListConversationProfilesRequest], + ~.ListConversationProfilesResponse]: + 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_conversation_profiles" not in self._stubs: + self._stubs["list_conversation_profiles"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.ConversationProfiles/ListConversationProfiles", + request_serializer=conversation_profile.ListConversationProfilesRequest.serialize, + response_deserializer=conversation_profile.ListConversationProfilesResponse.deserialize, + ) + return self._stubs["list_conversation_profiles"] + + @property + def get_conversation_profile( + self, + ) -> Callable[ + [conversation_profile.GetConversationProfileRequest], + conversation_profile.ConversationProfile, + ]: + r"""Return a callable for the get conversation profile method over gRPC. + + Retrieves the specified conversation profile. + + Returns: + Callable[[~.GetConversationProfileRequest], + ~.ConversationProfile]: + 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_conversation_profile" not in self._stubs: + self._stubs["get_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.ConversationProfiles/GetConversationProfile", + request_serializer=conversation_profile.GetConversationProfileRequest.serialize, + response_deserializer=conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["get_conversation_profile"] + + @property + def create_conversation_profile( + self, + ) -> Callable[ + [gcd_conversation_profile.CreateConversationProfileRequest], + gcd_conversation_profile.ConversationProfile, + ]: + r"""Return a callable for the create conversation profile method over gRPC. + + Creates a conversation profile in the specified project. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile] + API. + + Returns: + Callable[[~.CreateConversationProfileRequest], + ~.ConversationProfile]: + 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_conversation_profile" not in self._stubs: + self._stubs["create_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.ConversationProfiles/CreateConversationProfile", + request_serializer=gcd_conversation_profile.CreateConversationProfileRequest.serialize, + response_deserializer=gcd_conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["create_conversation_profile"] + + @property + def update_conversation_profile( + self, + ) -> Callable[ + [gcd_conversation_profile.UpdateConversationProfileRequest], + gcd_conversation_profile.ConversationProfile, + ]: + r"""Return a callable for the update conversation profile method over gRPC. + + Updates the specified conversation profile. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile] + API. + + Returns: + Callable[[~.UpdateConversationProfileRequest], + ~.ConversationProfile]: + 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_conversation_profile" not in self._stubs: + self._stubs["update_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.ConversationProfiles/UpdateConversationProfile", + request_serializer=gcd_conversation_profile.UpdateConversationProfileRequest.serialize, + response_deserializer=gcd_conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["update_conversation_profile"] + + @property + def delete_conversation_profile( + self, + ) -> Callable[[conversation_profile.DeleteConversationProfileRequest], empty.Empty]: + r"""Return a callable for the delete conversation profile method over gRPC. + + Deletes the specified conversation profile. + + Returns: + Callable[[~.DeleteConversationProfileRequest], + ~.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_conversation_profile" not in self._stubs: + self._stubs["delete_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.ConversationProfiles/DeleteConversationProfile", + request_serializer=conversation_profile.DeleteConversationProfileRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_conversation_profile"] + + +__all__ = ("ConversationProfilesGrpcTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/conversation_profiles/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/transports/grpc_asyncio.py new file mode 100644 index 000000000..8f2997c38 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversation_profiles/transports/grpc_asyncio.py @@ -0,0 +1,418 @@ +# -*- 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.dialogflow_v2beta1.types import conversation_profile +from google.cloud.dialogflow_v2beta1.types import ( + conversation_profile as gcd_conversation_profile, +) +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import ConversationProfilesTransport, DEFAULT_CLIENT_INFO +from .grpc import ConversationProfilesGrpcTransport + + +class ConversationProfilesGrpcAsyncIOTransport(ConversationProfilesTransport): + """gRPC AsyncIO backend transport for ConversationProfiles. + + Service for managing + [ConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfile]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + self._stubs = {} + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def list_conversation_profiles( + self, + ) -> Callable[ + [conversation_profile.ListConversationProfilesRequest], + Awaitable[conversation_profile.ListConversationProfilesResponse], + ]: + r"""Return a callable for the list conversation profiles method over gRPC. + + Returns the list of all conversation profiles in the + specified project. + + Returns: + Callable[[~.ListConversationProfilesRequest], + Awaitable[~.ListConversationProfilesResponse]]: + 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_conversation_profiles" not in self._stubs: + self._stubs["list_conversation_profiles"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.ConversationProfiles/ListConversationProfiles", + request_serializer=conversation_profile.ListConversationProfilesRequest.serialize, + response_deserializer=conversation_profile.ListConversationProfilesResponse.deserialize, + ) + return self._stubs["list_conversation_profiles"] + + @property + def get_conversation_profile( + self, + ) -> Callable[ + [conversation_profile.GetConversationProfileRequest], + Awaitable[conversation_profile.ConversationProfile], + ]: + r"""Return a callable for the get conversation profile method over gRPC. + + Retrieves the specified conversation profile. + + Returns: + Callable[[~.GetConversationProfileRequest], + Awaitable[~.ConversationProfile]]: + 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_conversation_profile" not in self._stubs: + self._stubs["get_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.ConversationProfiles/GetConversationProfile", + request_serializer=conversation_profile.GetConversationProfileRequest.serialize, + response_deserializer=conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["get_conversation_profile"] + + @property + def create_conversation_profile( + self, + ) -> Callable[ + [gcd_conversation_profile.CreateConversationProfileRequest], + Awaitable[gcd_conversation_profile.ConversationProfile], + ]: + r"""Return a callable for the create conversation profile method over gRPC. + + Creates a conversation profile in the specified project. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile] + API. + + Returns: + Callable[[~.CreateConversationProfileRequest], + Awaitable[~.ConversationProfile]]: + 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_conversation_profile" not in self._stubs: + self._stubs["create_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.ConversationProfiles/CreateConversationProfile", + request_serializer=gcd_conversation_profile.CreateConversationProfileRequest.serialize, + response_deserializer=gcd_conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["create_conversation_profile"] + + @property + def update_conversation_profile( + self, + ) -> Callable[ + [gcd_conversation_profile.UpdateConversationProfileRequest], + Awaitable[gcd_conversation_profile.ConversationProfile], + ]: + r"""Return a callable for the update conversation profile method over gRPC. + + Updates the specified conversation profile. + + [ConversationProfile.CreateTime][] and + [ConversationProfile.UpdateTime][] aren't populated in the + response. You can retrieve them via + [GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile] + API. + + Returns: + Callable[[~.UpdateConversationProfileRequest], + Awaitable[~.ConversationProfile]]: + 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_conversation_profile" not in self._stubs: + self._stubs["update_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.ConversationProfiles/UpdateConversationProfile", + request_serializer=gcd_conversation_profile.UpdateConversationProfileRequest.serialize, + response_deserializer=gcd_conversation_profile.ConversationProfile.deserialize, + ) + return self._stubs["update_conversation_profile"] + + @property + def delete_conversation_profile( + self, + ) -> Callable[ + [conversation_profile.DeleteConversationProfileRequest], Awaitable[empty.Empty] + ]: + r"""Return a callable for the delete conversation profile method over gRPC. + + Deletes the specified conversation profile. + + Returns: + Callable[[~.DeleteConversationProfileRequest], + 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_conversation_profile" not in self._stubs: + self._stubs["delete_conversation_profile"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.ConversationProfiles/DeleteConversationProfile", + request_serializer=conversation_profile.DeleteConversationProfileRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_conversation_profile"] + + +__all__ = ("ConversationProfilesGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/__init__.py b/google/cloud/dialogflow_v2beta1/services/conversations/__init__.py new file mode 100644 index 000000000..cce809abe --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversations/__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 ConversationsClient +from .async_client import ConversationsAsyncClient + +__all__ = ( + "ConversationsClient", + "ConversationsAsyncClient", +) diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/async_client.py b/google/cloud/dialogflow_v2beta1/services/conversations/async_client.py new file mode 100644 index 000000000..ad27edf78 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversations/async_client.py @@ -0,0 +1,977 @@ +# -*- 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.dialogflow_v2beta1.services.conversations import pagers +from google.cloud.dialogflow_v2beta1.types import conversation +from google.cloud.dialogflow_v2beta1.types import conversation as gcd_conversation +from google.cloud.dialogflow_v2beta1.types import participant +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import ConversationsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import ConversationsGrpcAsyncIOTransport +from .client import ConversationsClient + + +class ConversationsAsyncClient: + """Service for managing + [Conversations][google.cloud.dialogflow.v2beta1.Conversation]. + """ + + _client: ConversationsClient + + DEFAULT_ENDPOINT = ConversationsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = ConversationsClient.DEFAULT_MTLS_ENDPOINT + + call_matcher_path = staticmethod(ConversationsClient.call_matcher_path) + parse_call_matcher_path = staticmethod(ConversationsClient.parse_call_matcher_path) + conversation_path = staticmethod(ConversationsClient.conversation_path) + parse_conversation_path = staticmethod(ConversationsClient.parse_conversation_path) + conversation_profile_path = staticmethod( + ConversationsClient.conversation_profile_path + ) + parse_conversation_profile_path = staticmethod( + ConversationsClient.parse_conversation_profile_path + ) + message_path = staticmethod(ConversationsClient.message_path) + parse_message_path = staticmethod(ConversationsClient.parse_message_path) + + common_billing_account_path = staticmethod( + ConversationsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + ConversationsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(ConversationsClient.common_folder_path) + parse_common_folder_path = staticmethod( + ConversationsClient.parse_common_folder_path + ) + + common_organization_path = staticmethod( + ConversationsClient.common_organization_path + ) + parse_common_organization_path = staticmethod( + ConversationsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(ConversationsClient.common_project_path) + parse_common_project_path = staticmethod( + ConversationsClient.parse_common_project_path + ) + + common_location_path = staticmethod(ConversationsClient.common_location_path) + parse_common_location_path = staticmethod( + ConversationsClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationsAsyncClient: The constructed client. + """ + return ConversationsClient.from_service_account_info.__func__(ConversationsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationsAsyncClient: The constructed client. + """ + return ConversationsClient.from_service_account_file.__func__(ConversationsAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> ConversationsTransport: + """Return the transport used by the client instance. + + Returns: + ConversationsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(ConversationsClient).get_transport_class, type(ConversationsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, ConversationsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the conversations 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, ~.ConversationsTransport]): 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 = ConversationsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def create_conversation( + self, + request: gcd_conversation.CreateConversationRequest = None, + *, + parent: str = None, + conversation: gcd_conversation.Conversation = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation.Conversation: + r"""Creates a new conversation. Conversations are auto-completed + after 24 hours. + + Conversation Lifecycle: There are two stages during a + conversation: Automated Agent Stage and Assist Stage. + + For Automated Agent Stage, there will be a dialogflow agent + responding to user queries. + + For Assist Stage, there's no dialogflow agent responding to user + queries. But we will provide suggestions which are generated + from conversation. + + If + [Conversation.conversation_profile][google.cloud.dialogflow.v2beta1.Conversation.conversation_profile] + is configured for a dialogflow agent, conversation will start + from ``Automated Agent Stage``, otherwise, it will start from + ``Assist Stage``. And during ``Automated Agent Stage``, once an + [Intent][google.cloud.dialogflow.v2beta1.Intent] with + [Intent.live_agent_handoff][google.cloud.dialogflow.v2beta1.Intent.live_agent_handoff] + is triggered, conversation will transfer to Assist Stage. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.CreateConversationRequest`): + The request object. The request message for + [Conversations.CreateConversation][google.cloud.dialogflow.v2beta1.Conversations.CreateConversation]. + parent (:class:`str`): + Required. Resource identifier of the project creating + the conversation. Format: + ``projects//locations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + conversation (:class:`google.cloud.dialogflow_v2beta1.types.Conversation`): + Required. The conversation to create. + This corresponds to the ``conversation`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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, conversation]) + if request is 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 = gcd_conversation.CreateConversationRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if conversation is not None: + request.conversation = conversation + + # 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_conversation, + 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 list_conversations( + self, + request: conversation.ListConversationsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListConversationsAsyncPager: + r"""Returns the list of all conversations in the + specified project. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.ListConversationsRequest`): + The request object. The request message for + [Conversations.ListConversations][google.cloud.dialogflow.v2beta1.Conversations.ListConversations]. + parent (:class:`str`): + Required. The project from which to list all + conversation. 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: + google.cloud.dialogflow_v2beta1.services.conversations.pagers.ListConversationsAsyncPager: + The response message for + [Conversations.ListConversations][google.cloud.dialogflow.v2beta1.Conversations.ListConversations]. + + 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 = conversation.ListConversationsRequest(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_conversations, + 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.ListConversationsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_conversation( + self, + request: conversation.GetConversationRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.Conversation: + r"""Retrieves the specific conversation. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.GetConversationRequest`): + The request object. The request message for + [Conversations.GetConversation][google.cloud.dialogflow.v2beta1.Conversations.GetConversation]. + name (:class:`str`): + Required. The name of the conversation. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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 = conversation.GetConversationRequest(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_conversation, + 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 complete_conversation( + self, + request: conversation.CompleteConversationRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.Conversation: + r"""Completes the specified conversation. Finished + conversations are purged from the database after 30 + days. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.CompleteConversationRequest`): + The request object. The request message for + [Conversations.CompleteConversation][google.cloud.dialogflow.v2beta1.Conversations.CompleteConversation]. + name (:class:`str`): + Required. Resource identifier of the conversation to + close. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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 = conversation.CompleteConversationRequest(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.complete_conversation, + 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_call_matcher( + self, + request: conversation.CreateCallMatcherRequest = None, + *, + parent: str = None, + call_matcher: conversation.CallMatcher = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.CallMatcher: + r"""Creates a call matcher that links incoming SIP calls + to the specified conversation if they fulfill specified + criteria. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.CreateCallMatcherRequest`): + The request object. The request message for + [Conversations.CreateCallMatcher][google.cloud.dialogflow.v2beta1.Conversations.CreateCallMatcher]. + parent (:class:`str`): + Required. Resource identifier of the conversation adding + the call matcher. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + call_matcher (:class:`google.cloud.dialogflow_v2beta1.types.CallMatcher`): + Required. The call matcher to create. + This corresponds to the ``call_matcher`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.CallMatcher: + Represents a call matcher that describes criteria for matching incoming SIP + calls to a conversation. When Dialogflow get a SIP + call from a third-party carrier, Dialogflow matches + the call to an existing conversation by either: + + - Extracting the conversation id from the [Call-Info + header](\ https://tools.ietf.org/html/rfc3261#section-20.9), + e.g. Call-Info: + + ;purpose=Goog-ContactCenter-Conversation. + + \* Or, if that doesn't work, matching incoming [SIP + headers](\ https://tools.ietf.org/html/rfc3261#section-7.3) + against any + [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] + for the conversation. + + If an incoming SIP call without valid Call-Info + header matches to zero or multiple conversations with + CallMatcher, we reject it. + + A call matcher contains equality conditions for SIP + headers that all have to be fulfilled in order for a + SIP call to match. + + The matched SIP headers consist of well-known headers + (To, From, Call-ID) and custom headers. A + [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] + is only valid if it specifies: + + - At least 1 custom header, + - or at least 2 well-known headers. + + """ + # Create or coerce a 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, call_matcher]) + if request is 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 = conversation.CreateCallMatcherRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if call_matcher is not None: + request.call_matcher = call_matcher + + # 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_call_matcher, + 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 list_call_matchers( + self, + request: conversation.ListCallMatchersRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListCallMatchersAsyncPager: + r"""Returns the list of all call matchers in the + specified conversation. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.ListCallMatchersRequest`): + The request object. The request message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2beta1.Conversations.ListCallMatchers]. + parent (:class:`str`): + Required. The conversation to list all call matchers + from. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.services.conversations.pagers.ListCallMatchersAsyncPager: + The response message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2beta1.Conversations.ListCallMatchers]. + + 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 = conversation.ListCallMatchersRequest(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_call_matchers, + 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.ListCallMatchersAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def delete_call_matcher( + self, + request: conversation.DeleteCallMatcherRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Requests deletion of a call matcher. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.DeleteCallMatcherRequest`): + The request object. The request message for + [Conversations.DeleteCallMatcher][google.cloud.dialogflow.v2beta1.Conversations.DeleteCallMatcher]. + name (:class:`str`): + Required. The unique identifier of the + [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] + to delete. Format: + ``projects//locations//conversations//callMatchers/``. + + 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 = conversation.DeleteCallMatcherRequest(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_call_matcher, + 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 batch_create_messages( + self, + request: conversation.BatchCreateMessagesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.BatchCreateMessagesResponse: + r"""Batch ingests messages to conversation. Customers can + use this RPC to ingest historical messages to + conversation. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.BatchCreateMessagesRequest`): + The request object. The request message for + [Conversations.BatchCreateMessagesRequest][]. + parent (:class:`str`): + Required. Resource identifier of the conversation to + create message. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.BatchCreateMessagesResponse: + The request message for + [Conversations.BatchCreateMessagesResponse][]. + + """ + # Create or coerce a 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 = conversation.BatchCreateMessagesRequest(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.batch_create_messages, + 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 list_messages( + self, + request: conversation.ListMessagesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListMessagesAsyncPager: + r"""Lists messages that belong to a given conversation. ``messages`` + are ordered by ``create_time`` in descending order. To fetch + updates without duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.ListMessagesRequest`): + The request object. The request message for + [Conversations.ListMessages][google.cloud.dialogflow.v2beta1.Conversations.ListMessages]. + parent (:class:`str`): + Required. The name of the conversation to list messages + for. Format: + ``projects//locations//conversations/`` + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.services.conversations.pagers.ListMessagesAsyncPager: + The response message for + [Conversations.ListMessages][google.cloud.dialogflow.v2beta1.Conversations.ListMessages]. + + 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 = conversation.ListMessagesRequest(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_messages, + 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.ListMessagesAsyncPager( + 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ConversationsAsyncClient",) diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/client.py b/google/cloud/dialogflow_v2beta1/services/conversations/client.py new file mode 100644 index 000000000..1855fe92d --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversations/client.py @@ -0,0 +1,1195 @@ +# -*- 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.dialogflow_v2beta1.services.conversations import pagers +from google.cloud.dialogflow_v2beta1.types import conversation +from google.cloud.dialogflow_v2beta1.types import conversation as gcd_conversation +from google.cloud.dialogflow_v2beta1.types import participant +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + +from .transports.base import ConversationsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import ConversationsGrpcTransport +from .transports.grpc_asyncio import ConversationsGrpcAsyncIOTransport + + +class ConversationsClientMeta(type): + """Metaclass for the Conversations 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[ConversationsTransport]] + _transport_registry["grpc"] = ConversationsGrpcTransport + _transport_registry["grpc_asyncio"] = ConversationsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[ConversationsTransport]: + """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 ConversationsClient(metaclass=ConversationsClientMeta): + """Service for managing + [Conversations][google.cloud.dialogflow.v2beta1.Conversation]. + """ + + @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_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ConversationsClient: 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) -> ConversationsTransport: + """Return the transport used by the client instance. + + Returns: + ConversationsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def call_matcher_path(project: str, conversation: str, call_matcher: str,) -> str: + """Return a fully-qualified call_matcher string.""" + return "projects/{project}/conversations/{conversation}/callMatchers/{call_matcher}".format( + project=project, conversation=conversation, call_matcher=call_matcher, + ) + + @staticmethod + def parse_call_matcher_path(path: str) -> Dict[str, str]: + """Parse a call_matcher path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversations/(?P.+?)/callMatchers/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def conversation_path(project: str, conversation: str,) -> str: + """Return a fully-qualified conversation string.""" + return "projects/{project}/conversations/{conversation}".format( + project=project, conversation=conversation, + ) + + @staticmethod + def parse_conversation_path(path: str) -> Dict[str, str]: + """Parse a conversation path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversations/(?P.+?)$", path + ) + return m.groupdict() if m else {} + + @staticmethod + def conversation_profile_path(project: str, conversation_profile: str,) -> str: + """Return a fully-qualified conversation_profile string.""" + return "projects/{project}/conversationProfiles/{conversation_profile}".format( + project=project, conversation_profile=conversation_profile, + ) + + @staticmethod + def parse_conversation_profile_path(path: str) -> Dict[str, str]: + """Parse a conversation_profile path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversationProfiles/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def message_path(project: str, conversation: str, message: str,) -> str: + """Return a fully-qualified message string.""" + return "projects/{project}/conversations/{conversation}/messages/{message}".format( + project=project, conversation=conversation, message=message, + ) + + @staticmethod + def parse_message_path(path: str) -> Dict[str, str]: + """Parse a message path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversations/(?P.+?)/messages/(?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, ConversationsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the conversations 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, ConversationsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, ConversationsTransport): + # transport is a ConversationsTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def create_conversation( + self, + request: gcd_conversation.CreateConversationRequest = None, + *, + parent: str = None, + conversation: gcd_conversation.Conversation = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_conversation.Conversation: + r"""Creates a new conversation. Conversations are auto-completed + after 24 hours. + + Conversation Lifecycle: There are two stages during a + conversation: Automated Agent Stage and Assist Stage. + + For Automated Agent Stage, there will be a dialogflow agent + responding to user queries. + + For Assist Stage, there's no dialogflow agent responding to user + queries. But we will provide suggestions which are generated + from conversation. + + If + [Conversation.conversation_profile][google.cloud.dialogflow.v2beta1.Conversation.conversation_profile] + is configured for a dialogflow agent, conversation will start + from ``Automated Agent Stage``, otherwise, it will start from + ``Assist Stage``. And during ``Automated Agent Stage``, once an + [Intent][google.cloud.dialogflow.v2beta1.Intent] with + [Intent.live_agent_handoff][google.cloud.dialogflow.v2beta1.Intent.live_agent_handoff] + is triggered, conversation will transfer to Assist Stage. + + Args: + request (google.cloud.dialogflow_v2beta1.types.CreateConversationRequest): + The request object. The request message for + [Conversations.CreateConversation][google.cloud.dialogflow.v2beta1.Conversations.CreateConversation]. + parent (str): + Required. Resource identifier of the project creating + the conversation. Format: + ``projects//locations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + conversation (google.cloud.dialogflow_v2beta1.types.Conversation): + Required. The conversation to create. + This corresponds to the ``conversation`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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, conversation]) + if request is 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 gcd_conversation.CreateConversationRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_conversation.CreateConversationRequest): + request = gcd_conversation.CreateConversationRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if conversation is not None: + request.conversation = conversation + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_conversation] + + # 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 list_conversations( + self, + request: conversation.ListConversationsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListConversationsPager: + r"""Returns the list of all conversations in the + specified project. + + Args: + request (google.cloud.dialogflow_v2beta1.types.ListConversationsRequest): + The request object. The request message for + [Conversations.ListConversations][google.cloud.dialogflow.v2beta1.Conversations.ListConversations]. + parent (str): + Required. The project from which to list all + conversation. 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: + google.cloud.dialogflow_v2beta1.services.conversations.pagers.ListConversationsPager: + The response message for + [Conversations.ListConversations][google.cloud.dialogflow.v2beta1.Conversations.ListConversations]. + + 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 conversation.ListConversationsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.ListConversationsRequest): + request = conversation.ListConversationsRequest(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_conversations] + + # 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.ListConversationsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def get_conversation( + self, + request: conversation.GetConversationRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.Conversation: + r"""Retrieves the specific conversation. + + Args: + request (google.cloud.dialogflow_v2beta1.types.GetConversationRequest): + The request object. The request message for + [Conversations.GetConversation][google.cloud.dialogflow.v2beta1.Conversations.GetConversation]. + name (str): + Required. The name of the conversation. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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 conversation.GetConversationRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.GetConversationRequest): + request = conversation.GetConversationRequest(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_conversation] + + # 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 complete_conversation( + self, + request: conversation.CompleteConversationRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.Conversation: + r"""Completes the specified conversation. Finished + conversations are purged from the database after 30 + days. + + Args: + request (google.cloud.dialogflow_v2beta1.types.CompleteConversationRequest): + The request object. The request message for + [Conversations.CompleteConversation][google.cloud.dialogflow.v2beta1.Conversations.CompleteConversation]. + name (str): + Required. Resource identifier of the conversation to + close. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Conversation: + Represents a conversation. + A conversation is an interaction between + an agent, including live agents and + Dialogflow agents, and a support + customer. Conversations can include + phone calls and text-based chat + sessions. + + """ + # Create or coerce a 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 conversation.CompleteConversationRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.CompleteConversationRequest): + request = conversation.CompleteConversationRequest(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.complete_conversation] + + # 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_call_matcher( + self, + request: conversation.CreateCallMatcherRequest = None, + *, + parent: str = None, + call_matcher: conversation.CallMatcher = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.CallMatcher: + r"""Creates a call matcher that links incoming SIP calls + to the specified conversation if they fulfill specified + criteria. + + Args: + request (google.cloud.dialogflow_v2beta1.types.CreateCallMatcherRequest): + The request object. The request message for + [Conversations.CreateCallMatcher][google.cloud.dialogflow.v2beta1.Conversations.CreateCallMatcher]. + parent (str): + Required. Resource identifier of the conversation adding + the call matcher. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + call_matcher (google.cloud.dialogflow_v2beta1.types.CallMatcher): + Required. The call matcher to create. + This corresponds to the ``call_matcher`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.CallMatcher: + Represents a call matcher that describes criteria for matching incoming SIP + calls to a conversation. When Dialogflow get a SIP + call from a third-party carrier, Dialogflow matches + the call to an existing conversation by either: + + - Extracting the conversation id from the [Call-Info + header](\ https://tools.ietf.org/html/rfc3261#section-20.9), + e.g. Call-Info: + + ;purpose=Goog-ContactCenter-Conversation. + + \* Or, if that doesn't work, matching incoming [SIP + headers](\ https://tools.ietf.org/html/rfc3261#section-7.3) + against any + [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] + for the conversation. + + If an incoming SIP call without valid Call-Info + header matches to zero or multiple conversations with + CallMatcher, we reject it. + + A call matcher contains equality conditions for SIP + headers that all have to be fulfilled in order for a + SIP call to match. + + The matched SIP headers consist of well-known headers + (To, From, Call-ID) and custom headers. A + [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] + is only valid if it specifies: + + - At least 1 custom header, + - or at least 2 well-known headers. + + """ + # Create or coerce a 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, call_matcher]) + if request is 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 conversation.CreateCallMatcherRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.CreateCallMatcherRequest): + request = conversation.CreateCallMatcherRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if call_matcher is not None: + request.call_matcher = call_matcher + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_call_matcher] + + # 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 list_call_matchers( + self, + request: conversation.ListCallMatchersRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListCallMatchersPager: + r"""Returns the list of all call matchers in the + specified conversation. + + Args: + request (google.cloud.dialogflow_v2beta1.types.ListCallMatchersRequest): + The request object. The request message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2beta1.Conversations.ListCallMatchers]. + parent (str): + Required. The conversation to list all call matchers + from. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.services.conversations.pagers.ListCallMatchersPager: + The response message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2beta1.Conversations.ListCallMatchers]. + + 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 conversation.ListCallMatchersRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.ListCallMatchersRequest): + request = conversation.ListCallMatchersRequest(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_call_matchers] + + # 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.ListCallMatchersPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def delete_call_matcher( + self, + request: conversation.DeleteCallMatcherRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> None: + r"""Requests deletion of a call matcher. + + Args: + request (google.cloud.dialogflow_v2beta1.types.DeleteCallMatcherRequest): + The request object. The request message for + [Conversations.DeleteCallMatcher][google.cloud.dialogflow.v2beta1.Conversations.DeleteCallMatcher]. + name (str): + Required. The unique identifier of the + [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] + to delete. Format: + ``projects//locations//conversations//callMatchers/``. + + 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 conversation.DeleteCallMatcherRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.DeleteCallMatcherRequest): + request = conversation.DeleteCallMatcherRequest(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_call_matcher] + + # 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 batch_create_messages( + self, + request: conversation.BatchCreateMessagesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> conversation.BatchCreateMessagesResponse: + r"""Batch ingests messages to conversation. Customers can + use this RPC to ingest historical messages to + conversation. + + Args: + request (google.cloud.dialogflow_v2beta1.types.BatchCreateMessagesRequest): + The request object. The request message for + [Conversations.BatchCreateMessagesRequest][]. + parent (str): + Required. Resource identifier of the conversation to + create message. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.BatchCreateMessagesResponse: + The request message for + [Conversations.BatchCreateMessagesResponse][]. + + """ + # Create or coerce a 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 conversation.BatchCreateMessagesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.BatchCreateMessagesRequest): + request = conversation.BatchCreateMessagesRequest(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.batch_create_messages] + + # 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 list_messages( + self, + request: conversation.ListMessagesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListMessagesPager: + r"""Lists messages that belong to a given conversation. ``messages`` + are ordered by ``create_time`` in descending order. To fetch + updates without duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Args: + request (google.cloud.dialogflow_v2beta1.types.ListMessagesRequest): + The request object. The request message for + [Conversations.ListMessages][google.cloud.dialogflow.v2beta1.Conversations.ListMessages]. + parent (str): + Required. The name of the conversation to list messages + for. Format: + ``projects//locations//conversations/`` + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.services.conversations.pagers.ListMessagesPager: + The response message for + [Conversations.ListMessages][google.cloud.dialogflow.v2beta1.Conversations.ListMessages]. + + 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 conversation.ListMessagesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, conversation.ListMessagesRequest): + request = conversation.ListMessagesRequest(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_messages] + + # 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.ListMessagesPager( + 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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ConversationsClient",) diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/pagers.py b/google/cloud/dialogflow_v2beta1/services/conversations/pagers.py new file mode 100644 index 000000000..eb97134b9 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversations/pagers.py @@ -0,0 +1,414 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.dialogflow_v2beta1.types import conversation +from google.cloud.dialogflow_v2beta1.types import participant + + +class ListConversationsPager: + """A pager for iterating through ``list_conversations`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListConversationsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``conversations`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListConversations`` requests and continue to iterate + through the ``conversations`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListConversationsResponse` + 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[..., conversation.ListConversationsResponse], + request: conversation.ListConversationsRequest, + response: conversation.ListConversationsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListConversationsRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListConversationsResponse): + 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 = conversation.ListConversationsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[conversation.ListConversationsResponse]: + 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[conversation.Conversation]: + for page in self.pages: + yield from page.conversations + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListConversationsAsyncPager: + """A pager for iterating through ``list_conversations`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListConversationsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``conversations`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListConversations`` requests and continue to iterate + through the ``conversations`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListConversationsResponse` + 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[conversation.ListConversationsResponse]], + request: conversation.ListConversationsRequest, + response: conversation.ListConversationsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListConversationsRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListConversationsResponse): + 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 = conversation.ListConversationsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[conversation.ListConversationsResponse]: + 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[conversation.Conversation]: + async def async_generator(): + async for page in self.pages: + for response in page.conversations: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListCallMatchersPager: + """A pager for iterating through ``list_call_matchers`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListCallMatchersResponse` object, and + provides an ``__iter__`` method to iterate through its + ``call_matchers`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListCallMatchers`` requests and continue to iterate + through the ``call_matchers`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListCallMatchersResponse` + 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[..., conversation.ListCallMatchersResponse], + request: conversation.ListCallMatchersRequest, + response: conversation.ListCallMatchersResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListCallMatchersRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListCallMatchersResponse): + 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 = conversation.ListCallMatchersRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[conversation.ListCallMatchersResponse]: + 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[conversation.CallMatcher]: + for page in self.pages: + yield from page.call_matchers + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListCallMatchersAsyncPager: + """A pager for iterating through ``list_call_matchers`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListCallMatchersResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``call_matchers`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListCallMatchers`` requests and continue to iterate + through the ``call_matchers`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListCallMatchersResponse` + 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[conversation.ListCallMatchersResponse]], + request: conversation.ListCallMatchersRequest, + response: conversation.ListCallMatchersResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListCallMatchersRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListCallMatchersResponse): + 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 = conversation.ListCallMatchersRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[conversation.ListCallMatchersResponse]: + 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[conversation.CallMatcher]: + async def async_generator(): + async for page in self.pages: + for response in page.call_matchers: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListMessagesPager: + """A pager for iterating through ``list_messages`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListMessagesResponse` object, and + provides an ``__iter__`` method to iterate through its + ``messages`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListMessages`` requests and continue to iterate + through the ``messages`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListMessagesResponse` + 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[..., conversation.ListMessagesResponse], + request: conversation.ListMessagesRequest, + response: conversation.ListMessagesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListMessagesRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListMessagesResponse): + 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 = conversation.ListMessagesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[conversation.ListMessagesResponse]: + 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[participant.Message]: + for page in self.pages: + yield from page.messages + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListMessagesAsyncPager: + """A pager for iterating through ``list_messages`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListMessagesResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``messages`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListMessages`` requests and continue to iterate + through the ``messages`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListMessagesResponse` + 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[conversation.ListMessagesResponse]], + request: conversation.ListMessagesRequest, + response: conversation.ListMessagesResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListMessagesRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListMessagesResponse): + 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 = conversation.ListMessagesRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[conversation.ListMessagesResponse]: + 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[participant.Message]: + async def async_generator(): + async for page in self.pages: + for response in page.messages: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/transports/__init__.py b/google/cloud/dialogflow_v2beta1/services/conversations/transports/__init__.py new file mode 100644 index 000000000..6597b6d01 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversations/transports/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import ConversationsTransport +from .grpc import ConversationsGrpcTransport +from .grpc_asyncio import ConversationsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[ConversationsTransport]] +_transport_registry["grpc"] = ConversationsGrpcTransport +_transport_registry["grpc_asyncio"] = ConversationsGrpcAsyncIOTransport + +__all__ = ( + "ConversationsTransport", + "ConversationsGrpcTransport", + "ConversationsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/transports/base.py b/google/cloud/dialogflow_v2beta1/services/conversations/transports/base.py new file mode 100644 index 000000000..16abd668a --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversations/transports/base.py @@ -0,0 +1,250 @@ +# -*- 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.dialogflow_v2beta1.types import conversation +from google.cloud.dialogflow_v2beta1.types import conversation as gcd_conversation +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-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class ConversationsTransport(abc.ABC): + """Abstract transport class for Conversations.""" + + 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_conversation: gapic_v1.method.wrap_method( + self.create_conversation, default_timeout=None, client_info=client_info, + ), + self.list_conversations: gapic_v1.method.wrap_method( + self.list_conversations, default_timeout=None, client_info=client_info, + ), + self.get_conversation: gapic_v1.method.wrap_method( + self.get_conversation, default_timeout=None, client_info=client_info, + ), + self.complete_conversation: gapic_v1.method.wrap_method( + self.complete_conversation, + default_timeout=None, + client_info=client_info, + ), + self.create_call_matcher: gapic_v1.method.wrap_method( + self.create_call_matcher, default_timeout=None, client_info=client_info, + ), + self.list_call_matchers: gapic_v1.method.wrap_method( + self.list_call_matchers, default_timeout=None, client_info=client_info, + ), + self.delete_call_matcher: gapic_v1.method.wrap_method( + self.delete_call_matcher, default_timeout=None, client_info=client_info, + ), + self.batch_create_messages: gapic_v1.method.wrap_method( + self.batch_create_messages, + default_timeout=None, + client_info=client_info, + ), + self.list_messages: gapic_v1.method.wrap_method( + self.list_messages, default_timeout=None, client_info=client_info, + ), + } + + @property + def create_conversation( + self, + ) -> typing.Callable[ + [gcd_conversation.CreateConversationRequest], + typing.Union[ + gcd_conversation.Conversation, + typing.Awaitable[gcd_conversation.Conversation], + ], + ]: + raise NotImplementedError() + + @property + def list_conversations( + self, + ) -> typing.Callable[ + [conversation.ListConversationsRequest], + typing.Union[ + conversation.ListConversationsResponse, + typing.Awaitable[conversation.ListConversationsResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_conversation( + self, + ) -> typing.Callable[ + [conversation.GetConversationRequest], + typing.Union[ + conversation.Conversation, typing.Awaitable[conversation.Conversation] + ], + ]: + raise NotImplementedError() + + @property + def complete_conversation( + self, + ) -> typing.Callable[ + [conversation.CompleteConversationRequest], + typing.Union[ + conversation.Conversation, typing.Awaitable[conversation.Conversation] + ], + ]: + raise NotImplementedError() + + @property + def create_call_matcher( + self, + ) -> typing.Callable[ + [conversation.CreateCallMatcherRequest], + typing.Union[ + conversation.CallMatcher, typing.Awaitable[conversation.CallMatcher] + ], + ]: + raise NotImplementedError() + + @property + def list_call_matchers( + self, + ) -> typing.Callable[ + [conversation.ListCallMatchersRequest], + typing.Union[ + conversation.ListCallMatchersResponse, + typing.Awaitable[conversation.ListCallMatchersResponse], + ], + ]: + raise NotImplementedError() + + @property + def delete_call_matcher( + self, + ) -> typing.Callable[ + [conversation.DeleteCallMatcherRequest], + typing.Union[empty.Empty, typing.Awaitable[empty.Empty]], + ]: + raise NotImplementedError() + + @property + def batch_create_messages( + self, + ) -> typing.Callable[ + [conversation.BatchCreateMessagesRequest], + typing.Union[ + conversation.BatchCreateMessagesResponse, + typing.Awaitable[conversation.BatchCreateMessagesResponse], + ], + ]: + raise NotImplementedError() + + @property + def list_messages( + self, + ) -> typing.Callable[ + [conversation.ListMessagesRequest], + typing.Union[ + conversation.ListMessagesResponse, + typing.Awaitable[conversation.ListMessagesResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("ConversationsTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc.py new file mode 100644 index 000000000..7e84c5300 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc.py @@ -0,0 +1,534 @@ +# -*- 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.dialogflow_v2beta1.types import conversation +from google.cloud.dialogflow_v2beta1.types import conversation as gcd_conversation +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import ConversationsTransport, DEFAULT_CLIENT_INFO + + +class ConversationsGrpcTransport(ConversationsTransport): + """gRPC backend transport for Conversations. + + Service for managing + [Conversations][google.cloud.dialogflow.v2beta1.Conversation]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + self._stubs = {} # type: Dict[str, Callable] + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + @classmethod + def create_channel( + cls, + host: str = "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 (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def create_conversation( + self, + ) -> Callable[ + [gcd_conversation.CreateConversationRequest], gcd_conversation.Conversation + ]: + r"""Return a callable for the create conversation method over gRPC. + + Creates a new conversation. Conversations are auto-completed + after 24 hours. + + Conversation Lifecycle: There are two stages during a + conversation: Automated Agent Stage and Assist Stage. + + For Automated Agent Stage, there will be a dialogflow agent + responding to user queries. + + For Assist Stage, there's no dialogflow agent responding to user + queries. But we will provide suggestions which are generated + from conversation. + + If + [Conversation.conversation_profile][google.cloud.dialogflow.v2beta1.Conversation.conversation_profile] + is configured for a dialogflow agent, conversation will start + from ``Automated Agent Stage``, otherwise, it will start from + ``Assist Stage``. And during ``Automated Agent Stage``, once an + [Intent][google.cloud.dialogflow.v2beta1.Intent] with + [Intent.live_agent_handoff][google.cloud.dialogflow.v2beta1.Intent.live_agent_handoff] + is triggered, conversation will transfer to Assist Stage. + + Returns: + Callable[[~.CreateConversationRequest], + ~.Conversation]: + 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_conversation" not in self._stubs: + self._stubs["create_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/CreateConversation", + request_serializer=gcd_conversation.CreateConversationRequest.serialize, + response_deserializer=gcd_conversation.Conversation.deserialize, + ) + return self._stubs["create_conversation"] + + @property + def list_conversations( + self, + ) -> Callable[ + [conversation.ListConversationsRequest], conversation.ListConversationsResponse + ]: + r"""Return a callable for the list conversations method over gRPC. + + Returns the list of all conversations in the + specified project. + + Returns: + Callable[[~.ListConversationsRequest], + ~.ListConversationsResponse]: + 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_conversations" not in self._stubs: + self._stubs["list_conversations"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/ListConversations", + request_serializer=conversation.ListConversationsRequest.serialize, + response_deserializer=conversation.ListConversationsResponse.deserialize, + ) + return self._stubs["list_conversations"] + + @property + def get_conversation( + self, + ) -> Callable[[conversation.GetConversationRequest], conversation.Conversation]: + r"""Return a callable for the get conversation method over gRPC. + + Retrieves the specific conversation. + + Returns: + Callable[[~.GetConversationRequest], + ~.Conversation]: + 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_conversation" not in self._stubs: + self._stubs["get_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/GetConversation", + request_serializer=conversation.GetConversationRequest.serialize, + response_deserializer=conversation.Conversation.deserialize, + ) + return self._stubs["get_conversation"] + + @property + def complete_conversation( + self, + ) -> Callable[ + [conversation.CompleteConversationRequest], conversation.Conversation + ]: + r"""Return a callable for the complete conversation method over gRPC. + + Completes the specified conversation. Finished + conversations are purged from the database after 30 + days. + + Returns: + Callable[[~.CompleteConversationRequest], + ~.Conversation]: + 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 "complete_conversation" not in self._stubs: + self._stubs["complete_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/CompleteConversation", + request_serializer=conversation.CompleteConversationRequest.serialize, + response_deserializer=conversation.Conversation.deserialize, + ) + return self._stubs["complete_conversation"] + + @property + def create_call_matcher( + self, + ) -> Callable[[conversation.CreateCallMatcherRequest], conversation.CallMatcher]: + r"""Return a callable for the create call matcher method over gRPC. + + Creates a call matcher that links incoming SIP calls + to the specified conversation if they fulfill specified + criteria. + + Returns: + Callable[[~.CreateCallMatcherRequest], + ~.CallMatcher]: + 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_call_matcher" not in self._stubs: + self._stubs["create_call_matcher"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/CreateCallMatcher", + request_serializer=conversation.CreateCallMatcherRequest.serialize, + response_deserializer=conversation.CallMatcher.deserialize, + ) + return self._stubs["create_call_matcher"] + + @property + def list_call_matchers( + self, + ) -> Callable[ + [conversation.ListCallMatchersRequest], conversation.ListCallMatchersResponse + ]: + r"""Return a callable for the list call matchers method over gRPC. + + Returns the list of all call matchers in the + specified conversation. + + Returns: + Callable[[~.ListCallMatchersRequest], + ~.ListCallMatchersResponse]: + 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_call_matchers" not in self._stubs: + self._stubs["list_call_matchers"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/ListCallMatchers", + request_serializer=conversation.ListCallMatchersRequest.serialize, + response_deserializer=conversation.ListCallMatchersResponse.deserialize, + ) + return self._stubs["list_call_matchers"] + + @property + def delete_call_matcher( + self, + ) -> Callable[[conversation.DeleteCallMatcherRequest], empty.Empty]: + r"""Return a callable for the delete call matcher method over gRPC. + + Requests deletion of a call matcher. + + Returns: + Callable[[~.DeleteCallMatcherRequest], + ~.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_call_matcher" not in self._stubs: + self._stubs["delete_call_matcher"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/DeleteCallMatcher", + request_serializer=conversation.DeleteCallMatcherRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_call_matcher"] + + @property + def batch_create_messages( + self, + ) -> Callable[ + [conversation.BatchCreateMessagesRequest], + conversation.BatchCreateMessagesResponse, + ]: + r"""Return a callable for the batch create messages method over gRPC. + + Batch ingests messages to conversation. Customers can + use this RPC to ingest historical messages to + conversation. + + Returns: + Callable[[~.BatchCreateMessagesRequest], + ~.BatchCreateMessagesResponse]: + 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 "batch_create_messages" not in self._stubs: + self._stubs["batch_create_messages"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/BatchCreateMessages", + request_serializer=conversation.BatchCreateMessagesRequest.serialize, + response_deserializer=conversation.BatchCreateMessagesResponse.deserialize, + ) + return self._stubs["batch_create_messages"] + + @property + def list_messages( + self, + ) -> Callable[ + [conversation.ListMessagesRequest], conversation.ListMessagesResponse + ]: + r"""Return a callable for the list messages method over gRPC. + + Lists messages that belong to a given conversation. ``messages`` + are ordered by ``create_time`` in descending order. To fetch + updates without duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Returns: + Callable[[~.ListMessagesRequest], + ~.ListMessagesResponse]: + 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_messages" not in self._stubs: + self._stubs["list_messages"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/ListMessages", + request_serializer=conversation.ListMessagesRequest.serialize, + response_deserializer=conversation.ListMessagesResponse.deserialize, + ) + return self._stubs["list_messages"] + + +__all__ = ("ConversationsGrpcTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc_asyncio.py new file mode 100644 index 000000000..7bdb25dca --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/conversations/transports/grpc_asyncio.py @@ -0,0 +1,545 @@ +# -*- 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.dialogflow_v2beta1.types import conversation +from google.cloud.dialogflow_v2beta1.types import conversation as gcd_conversation +from google.protobuf import empty_pb2 as empty # type: ignore + +from .base import ConversationsTransport, DEFAULT_CLIENT_INFO +from .grpc import ConversationsGrpcTransport + + +class ConversationsGrpcAsyncIOTransport(ConversationsTransport): + """gRPC AsyncIO backend transport for Conversations. + + Service for managing + [Conversations][google.cloud.dialogflow.v2beta1.Conversation]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + self._stubs = {} + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def create_conversation( + self, + ) -> Callable[ + [gcd_conversation.CreateConversationRequest], + Awaitable[gcd_conversation.Conversation], + ]: + r"""Return a callable for the create conversation method over gRPC. + + Creates a new conversation. Conversations are auto-completed + after 24 hours. + + Conversation Lifecycle: There are two stages during a + conversation: Automated Agent Stage and Assist Stage. + + For Automated Agent Stage, there will be a dialogflow agent + responding to user queries. + + For Assist Stage, there's no dialogflow agent responding to user + queries. But we will provide suggestions which are generated + from conversation. + + If + [Conversation.conversation_profile][google.cloud.dialogflow.v2beta1.Conversation.conversation_profile] + is configured for a dialogflow agent, conversation will start + from ``Automated Agent Stage``, otherwise, it will start from + ``Assist Stage``. And during ``Automated Agent Stage``, once an + [Intent][google.cloud.dialogflow.v2beta1.Intent] with + [Intent.live_agent_handoff][google.cloud.dialogflow.v2beta1.Intent.live_agent_handoff] + is triggered, conversation will transfer to Assist Stage. + + Returns: + Callable[[~.CreateConversationRequest], + Awaitable[~.Conversation]]: + 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_conversation" not in self._stubs: + self._stubs["create_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/CreateConversation", + request_serializer=gcd_conversation.CreateConversationRequest.serialize, + response_deserializer=gcd_conversation.Conversation.deserialize, + ) + return self._stubs["create_conversation"] + + @property + def list_conversations( + self, + ) -> Callable[ + [conversation.ListConversationsRequest], + Awaitable[conversation.ListConversationsResponse], + ]: + r"""Return a callable for the list conversations method over gRPC. + + Returns the list of all conversations in the + specified project. + + Returns: + Callable[[~.ListConversationsRequest], + Awaitable[~.ListConversationsResponse]]: + 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_conversations" not in self._stubs: + self._stubs["list_conversations"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/ListConversations", + request_serializer=conversation.ListConversationsRequest.serialize, + response_deserializer=conversation.ListConversationsResponse.deserialize, + ) + return self._stubs["list_conversations"] + + @property + def get_conversation( + self, + ) -> Callable[ + [conversation.GetConversationRequest], Awaitable[conversation.Conversation] + ]: + r"""Return a callable for the get conversation method over gRPC. + + Retrieves the specific conversation. + + Returns: + Callable[[~.GetConversationRequest], + Awaitable[~.Conversation]]: + 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_conversation" not in self._stubs: + self._stubs["get_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/GetConversation", + request_serializer=conversation.GetConversationRequest.serialize, + response_deserializer=conversation.Conversation.deserialize, + ) + return self._stubs["get_conversation"] + + @property + def complete_conversation( + self, + ) -> Callable[ + [conversation.CompleteConversationRequest], Awaitable[conversation.Conversation] + ]: + r"""Return a callable for the complete conversation method over gRPC. + + Completes the specified conversation. Finished + conversations are purged from the database after 30 + days. + + Returns: + Callable[[~.CompleteConversationRequest], + Awaitable[~.Conversation]]: + 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 "complete_conversation" not in self._stubs: + self._stubs["complete_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/CompleteConversation", + request_serializer=conversation.CompleteConversationRequest.serialize, + response_deserializer=conversation.Conversation.deserialize, + ) + return self._stubs["complete_conversation"] + + @property + def create_call_matcher( + self, + ) -> Callable[ + [conversation.CreateCallMatcherRequest], Awaitable[conversation.CallMatcher] + ]: + r"""Return a callable for the create call matcher method over gRPC. + + Creates a call matcher that links incoming SIP calls + to the specified conversation if they fulfill specified + criteria. + + Returns: + Callable[[~.CreateCallMatcherRequest], + Awaitable[~.CallMatcher]]: + 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_call_matcher" not in self._stubs: + self._stubs["create_call_matcher"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/CreateCallMatcher", + request_serializer=conversation.CreateCallMatcherRequest.serialize, + response_deserializer=conversation.CallMatcher.deserialize, + ) + return self._stubs["create_call_matcher"] + + @property + def list_call_matchers( + self, + ) -> Callable[ + [conversation.ListCallMatchersRequest], + Awaitable[conversation.ListCallMatchersResponse], + ]: + r"""Return a callable for the list call matchers method over gRPC. + + Returns the list of all call matchers in the + specified conversation. + + Returns: + Callable[[~.ListCallMatchersRequest], + Awaitable[~.ListCallMatchersResponse]]: + 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_call_matchers" not in self._stubs: + self._stubs["list_call_matchers"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/ListCallMatchers", + request_serializer=conversation.ListCallMatchersRequest.serialize, + response_deserializer=conversation.ListCallMatchersResponse.deserialize, + ) + return self._stubs["list_call_matchers"] + + @property + def delete_call_matcher( + self, + ) -> Callable[[conversation.DeleteCallMatcherRequest], Awaitable[empty.Empty]]: + r"""Return a callable for the delete call matcher method over gRPC. + + Requests deletion of a call matcher. + + Returns: + Callable[[~.DeleteCallMatcherRequest], + 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_call_matcher" not in self._stubs: + self._stubs["delete_call_matcher"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/DeleteCallMatcher", + request_serializer=conversation.DeleteCallMatcherRequest.serialize, + response_deserializer=empty.Empty.FromString, + ) + return self._stubs["delete_call_matcher"] + + @property + def batch_create_messages( + self, + ) -> Callable[ + [conversation.BatchCreateMessagesRequest], + Awaitable[conversation.BatchCreateMessagesResponse], + ]: + r"""Return a callable for the batch create messages method over gRPC. + + Batch ingests messages to conversation. Customers can + use this RPC to ingest historical messages to + conversation. + + Returns: + Callable[[~.BatchCreateMessagesRequest], + Awaitable[~.BatchCreateMessagesResponse]]: + 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 "batch_create_messages" not in self._stubs: + self._stubs["batch_create_messages"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/BatchCreateMessages", + request_serializer=conversation.BatchCreateMessagesRequest.serialize, + response_deserializer=conversation.BatchCreateMessagesResponse.deserialize, + ) + return self._stubs["batch_create_messages"] + + @property + def list_messages( + self, + ) -> Callable[ + [conversation.ListMessagesRequest], Awaitable[conversation.ListMessagesResponse] + ]: + r"""Return a callable for the list messages method over gRPC. + + Lists messages that belong to a given conversation. ``messages`` + are ordered by ``create_time`` in descending order. To fetch + updates without duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Returns: + Callable[[~.ListMessagesRequest], + Awaitable[~.ListMessagesResponse]]: + 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_messages" not in self._stubs: + self._stubs["list_messages"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Conversations/ListMessages", + request_serializer=conversation.ListMessagesRequest.serialize, + response_deserializer=conversation.ListMessagesResponse.deserialize, + ) + return self._stubs["list_messages"] + + +__all__ = ("ConversationsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/documents/async_client.py b/google/cloud/dialogflow_v2beta1/services/documents/async_client.py index fd7a43483..391764994 100644 --- a/google/cloud/dialogflow_v2beta1/services/documents/async_client.py +++ b/google/cloud/dialogflow_v2beta1/services/documents/async_client.py @@ -78,7 +78,36 @@ class DocumentsAsyncClient: DocumentsClient.parse_common_location_path ) - from_service_account_file = DocumentsClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + DocumentsAsyncClient: The constructed client. + """ + return DocumentsClient.from_service_account_info.__func__(DocumentsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + DocumentsAsyncClient: The constructed client. + """ + return DocumentsClient.from_service_account_file.__func__(DocumentsAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -157,13 +186,14 @@ async def list_documents( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.document.ListDocumentsRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.ListDocumentsRequest`): The request object. Request message for [Documents.ListDocuments][google.cloud.dialogflow.v2beta1.Documents.ListDocuments]. parent (:class:`str`): Required. The knowledge base to list all documents for. Format: ``projects//locations//knowledgeBases/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -175,7 +205,7 @@ async def list_documents( sent along with the request as metadata. Returns: - ~.pagers.ListDocumentsAsyncPager: + google.cloud.dialogflow_v2beta1.services.documents.pagers.ListDocumentsAsyncPager: Response message for [Documents.ListDocuments][google.cloud.dialogflow.v2beta1.Documents.ListDocuments]. @@ -242,12 +272,13 @@ async def get_document( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.document.GetDocumentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.GetDocumentRequest`): The request object. Request message for [Documents.GetDocument][google.cloud.dialogflow.v2beta1.Documents.GetDocument]. name (:class:`str`): Required. The name of the document to retrieve. Format ``projects//locations//knowledgeBases//documents/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -259,16 +290,16 @@ async def get_document( sent along with the request as metadata. Returns: - ~.document.Document: + google.cloud.dialogflow_v2beta1.types.Document: A knowledge document to be used by a [KnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBase]. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases.documents`` - resource is deprecated; only use - ``projects.knowledgeBases.documents``. + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. """ # Create or coerce a protobuf request object. @@ -325,17 +356,18 @@ async def create_document( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.gcd_document.CreateDocumentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.CreateDocumentRequest`): The request object. Request message for [Documents.CreateDocument][google.cloud.dialogflow.v2beta1.Documents.CreateDocument]. parent (:class:`str`): Required. The knowledge base to create a document for. Format: ``projects//locations//knowledgeBases/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - document (:class:`~.gcd_document.Document`): + document (:class:`google.cloud.dialogflow_v2beta1.types.Document`): Required. The document to create. This corresponds to the ``document`` field on the ``request`` instance; if ``request`` is provided, this @@ -348,20 +380,20 @@ async def create_document( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.gcd_document.Document``: A knowledge document - to be used by a + :class:`google.cloud.dialogflow_v2beta1.types.Document` + A knowledge document to be used by a [KnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBase]. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases.documents`` - resource is deprecated; only use - ``projects.knowledgeBases.documents``. + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. """ # Create or coerce a protobuf request object. @@ -412,6 +444,70 @@ async def create_document( # Done; return the response. return response + async def import_documents( + self, + request: document.ImportDocumentsRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Create documents by importing data from external + sources. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.ImportDocumentsRequest`): + The request object. Request message for + [Documents.ImportDocuments][google.cloud.dialogflow.v2beta1.Documents.ImportDocuments]. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation_async.AsyncOperation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.dialogflow_v2beta1.types.ImportDocumentsResponse` + Response message for + [Documents.ImportDocuments][google.cloud.dialogflow.v2beta1.Documents.ImportDocuments]. + + """ + # Create or coerce a protobuf request object. + + request = document.ImportDocumentsRequest(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.import_documents, + 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, + document.ImportDocumentsResponse, + metadata_type=document.KnowledgeOperationMetadata, + ) + + # Done; return the response. + return response + async def delete_document( self, request: document.DeleteDocumentRequest = None, @@ -427,12 +523,13 @@ async def delete_document( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.document.DeleteDocumentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.DeleteDocumentRequest`): The request object. Request message for [Documents.DeleteDocument][google.cloud.dialogflow.v2beta1.Documents.DeleteDocument]. name (:class:`str`): Required. The name of the document to delete. Format: ``projects//locations//knowledgeBases//documents/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -444,24 +541,22 @@ async def delete_document( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -526,18 +621,19 @@ async def update_document( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.gcd_document.UpdateDocumentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.UpdateDocumentRequest`): The request object. Request message for [Documents.UpdateDocument][google.cloud.dialogflow.v2beta1.Documents.UpdateDocument]. - document (:class:`~.gcd_document.Document`): + document (:class:`google.cloud.dialogflow_v2beta1.types.Document`): Required. The document to update. This corresponds to the ``document`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): Optional. Not specified means ``update all``. Currently, only ``display_name`` can be updated, an InvalidArgument will be returned for attempting to update other fields. + This corresponds to the ``update_mask`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -549,20 +645,20 @@ async def update_document( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.gcd_document.Document``: A knowledge document - to be used by a + :class:`google.cloud.dialogflow_v2beta1.types.Document` + A knowledge document to be used by a [KnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBase]. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases.documents`` - resource is deprecated; only use - ``projects.knowledgeBases.documents``. + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. """ # Create or coerce a protobuf request object. @@ -639,20 +735,22 @@ async def reload_document( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.document.ReloadDocumentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.ReloadDocumentRequest`): The request object. Request message for [Documents.ReloadDocument][google.cloud.dialogflow.v2beta1.Documents.ReloadDocument]. name (:class:`str`): Required. The name of the document to reload. Format: ``projects//locations//knowledgeBases//documents/`` + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - gcs_source (:class:`~.gcs.GcsSource`): + gcs_source (:class:`google.cloud.dialogflow_v2beta1.types.GcsSource`): The path for a Cloud Storage source file for reloading document content. If not provided, the Document's existing source will be reloaded. + This corresponds to the ``gcs_source`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -664,20 +762,20 @@ async def reload_document( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.document.Document``: A knowledge document to - be used by a + :class:`google.cloud.dialogflow_v2beta1.types.Document` + A knowledge document to be used by a [KnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBase]. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases.documents`` - resource is deprecated; only use - ``projects.knowledgeBases.documents``. + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. """ # Create or coerce a protobuf request object. diff --git a/google/cloud/dialogflow_v2beta1/services/documents/client.py b/google/cloud/dialogflow_v2beta1/services/documents/client.py index d8a5e0e11..a7890ee89 100644 --- a/google/cloud/dialogflow_v2beta1/services/documents/client.py +++ b/google/cloud/dialogflow_v2beta1/services/documents/client.py @@ -116,6 +116,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + DocumentsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -128,7 +144,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + DocumentsClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -236,10 +252,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.DocumentsTransport]): The + transport (Union[str, DocumentsTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (client_options_lib.ClientOptions): Custom options for the + client_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -275,21 +291,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -332,7 +344,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -352,13 +364,14 @@ def list_documents( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.document.ListDocumentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListDocumentsRequest): The request object. Request message for [Documents.ListDocuments][google.cloud.dialogflow.v2beta1.Documents.ListDocuments]. - parent (:class:`str`): + parent (str): Required. The knowledge base to list all documents for. Format: ``projects//locations//knowledgeBases/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -370,7 +383,7 @@ def list_documents( sent along with the request as metadata. Returns: - ~.pagers.ListDocumentsPager: + google.cloud.dialogflow_v2beta1.services.documents.pagers.ListDocumentsPager: Response message for [Documents.ListDocuments][google.cloud.dialogflow.v2beta1.Documents.ListDocuments]. @@ -438,12 +451,13 @@ def get_document( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.document.GetDocumentRequest`): + request (google.cloud.dialogflow_v2beta1.types.GetDocumentRequest): The request object. Request message for [Documents.GetDocument][google.cloud.dialogflow.v2beta1.Documents.GetDocument]. - name (:class:`str`): + name (str): Required. The name of the document to retrieve. Format ``projects//locations//knowledgeBases//documents/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -455,16 +469,16 @@ def get_document( sent along with the request as metadata. Returns: - ~.document.Document: + google.cloud.dialogflow_v2beta1.types.Document: A knowledge document to be used by a [KnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBase]. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases.documents`` - resource is deprecated; only use - ``projects.knowledgeBases.documents``. + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. """ # Create or coerce a protobuf request object. @@ -522,17 +536,18 @@ def create_document( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.gcd_document.CreateDocumentRequest`): + request (google.cloud.dialogflow_v2beta1.types.CreateDocumentRequest): The request object. Request message for [Documents.CreateDocument][google.cloud.dialogflow.v2beta1.Documents.CreateDocument]. - parent (:class:`str`): + parent (str): Required. The knowledge base to create a document for. Format: ``projects//locations//knowledgeBases/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - document (:class:`~.gcd_document.Document`): + document (google.cloud.dialogflow_v2beta1.types.Document): Required. The document to create. This corresponds to the ``document`` field on the ``request`` instance; if ``request`` is provided, this @@ -545,20 +560,20 @@ def create_document( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.gcd_document.Document``: A knowledge document - to be used by a + :class:`google.cloud.dialogflow_v2beta1.types.Document` + A knowledge document to be used by a [KnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBase]. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases.documents`` - resource is deprecated; only use - ``projects.knowledgeBases.documents``. + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. """ # Create or coerce a protobuf request object. @@ -610,6 +625,71 @@ def create_document( # Done; return the response. return response + def import_documents( + self, + request: document.ImportDocumentsRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Create documents by importing data from external + sources. + + Args: + request (google.cloud.dialogflow_v2beta1.types.ImportDocumentsRequest): + The request object. Request message for + [Documents.ImportDocuments][google.cloud.dialogflow.v2beta1.Documents.ImportDocuments]. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.api_core.operation.Operation: + An object representing a long-running operation. + + The result type for the operation will be + :class:`google.cloud.dialogflow_v2beta1.types.ImportDocumentsResponse` + Response message for + [Documents.ImportDocuments][google.cloud.dialogflow.v2beta1.Documents.ImportDocuments]. + + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a document.ImportDocumentsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, document.ImportDocumentsRequest): + request = document.ImportDocumentsRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.import_documents] + + # 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, + document.ImportDocumentsResponse, + metadata_type=document.KnowledgeOperationMetadata, + ) + + # Done; return the response. + return response + def delete_document( self, request: document.DeleteDocumentRequest = None, @@ -625,12 +705,13 @@ def delete_document( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.document.DeleteDocumentRequest`): + request (google.cloud.dialogflow_v2beta1.types.DeleteDocumentRequest): The request object. Request message for [Documents.DeleteDocument][google.cloud.dialogflow.v2beta1.Documents.DeleteDocument]. - name (:class:`str`): + name (str): Required. The name of the document to delete. Format: ``projects//locations//knowledgeBases//documents/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -642,24 +723,22 @@ def delete_document( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -725,18 +804,19 @@ def update_document( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.gcd_document.UpdateDocumentRequest`): + request (google.cloud.dialogflow_v2beta1.types.UpdateDocumentRequest): The request object. Request message for [Documents.UpdateDocument][google.cloud.dialogflow.v2beta1.Documents.UpdateDocument]. - document (:class:`~.gcd_document.Document`): + document (google.cloud.dialogflow_v2beta1.types.Document): Required. The document to update. This corresponds to the ``document`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. Not specified means ``update all``. Currently, only ``display_name`` can be updated, an InvalidArgument will be returned for attempting to update other fields. + This corresponds to the ``update_mask`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -748,20 +828,20 @@ def update_document( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.gcd_document.Document``: A knowledge document - to be used by a + :class:`google.cloud.dialogflow_v2beta1.types.Document` + A knowledge document to be used by a [KnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBase]. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases.documents`` - resource is deprecated; only use - ``projects.knowledgeBases.documents``. + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. """ # Create or coerce a protobuf request object. @@ -839,20 +919,22 @@ def reload_document( is deprecated; only use ``projects.knowledgeBases.documents``. Args: - request (:class:`~.document.ReloadDocumentRequest`): + request (google.cloud.dialogflow_v2beta1.types.ReloadDocumentRequest): The request object. Request message for [Documents.ReloadDocument][google.cloud.dialogflow.v2beta1.Documents.ReloadDocument]. - name (:class:`str`): + name (str): Required. The name of the document to reload. Format: ``projects//locations//knowledgeBases//documents/`` + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - gcs_source (:class:`~.gcs.GcsSource`): + gcs_source (google.cloud.dialogflow_v2beta1.types.GcsSource): The path for a Cloud Storage source file for reloading document content. If not provided, the Document's existing source will be reloaded. + This corresponds to the ``gcs_source`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -864,20 +946,20 @@ def reload_document( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.document.Document``: A knowledge document to - be used by a + :class:`google.cloud.dialogflow_v2beta1.types.Document` + A knowledge document to be used by a [KnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBase]. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases.documents`` - resource is deprecated; only use - ``projects.knowledgeBases.documents``. + Note: The projects.agent.knowledgeBases.documents + resource is deprecated; only use + projects.knowledgeBases.documents. """ # Create or coerce a protobuf request object. diff --git a/google/cloud/dialogflow_v2beta1/services/documents/pagers.py b/google/cloud/dialogflow_v2beta1/services/documents/pagers.py index 44c0c6500..9169210c7 100644 --- a/google/cloud/dialogflow_v2beta1/services/documents/pagers.py +++ b/google/cloud/dialogflow_v2beta1/services/documents/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2beta1.types import document @@ -24,7 +33,7 @@ class ListDocumentsPager: """A pager for iterating through ``list_documents`` requests. This class thinly wraps an initial - :class:`~.document.ListDocumentsResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListDocumentsResponse` object, and provides an ``__iter__`` method to iterate through its ``documents`` field. @@ -33,7 +42,7 @@ class ListDocumentsPager: through the ``documents`` field on the corresponding responses. - All the usual :class:`~.document.ListDocumentsResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListDocumentsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.document.ListDocumentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListDocumentsRequest): The initial request object. - response (:class:`~.document.ListDocumentsResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListDocumentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListDocumentsAsyncPager: """A pager for iterating through ``list_documents`` requests. This class thinly wraps an initial - :class:`~.document.ListDocumentsResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListDocumentsResponse` object, and provides an ``__aiter__`` method to iterate through its ``documents`` field. @@ -95,7 +104,7 @@ class ListDocumentsAsyncPager: through the ``documents`` field on the corresponding responses. - All the usual :class:`~.document.ListDocumentsResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListDocumentsResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.document.ListDocumentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListDocumentsRequest): The initial request object. - response (:class:`~.document.ListDocumentsResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListDocumentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2beta1/services/documents/transports/base.py b/google/cloud/dialogflow_v2beta1/services/documents/transports/base.py index 763cceb25..8f59da111 100644 --- a/google/cloud/dialogflow_v2beta1/services/documents/transports/base.py +++ b/google/cloud/dialogflow_v2beta1/services/documents/transports/base.py @@ -121,6 +121,9 @@ def _prep_wrapped_messages(self, client_info): self.create_document: gapic_v1.method.wrap_method( self.create_document, default_timeout=None, client_info=client_info, ), + self.import_documents: gapic_v1.method.wrap_method( + self.import_documents, default_timeout=None, client_info=client_info, + ), self.delete_document: gapic_v1.method.wrap_method( self.delete_document, default_timeout=None, client_info=client_info, ), @@ -167,6 +170,15 @@ def create_document( ]: raise NotImplementedError() + @property + def import_documents( + self, + ) -> typing.Callable[ + [document.ImportDocumentsRequest], + typing.Union[operations.Operation, typing.Awaitable[operations.Operation]], + ]: + raise NotImplementedError() + @property def delete_document( self, diff --git a/google/cloud/dialogflow_v2beta1/services/documents/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/documents/transports/grpc.py index ea490bcbc..bee3ec46e 100644 --- a/google/cloud/dialogflow_v2beta1/services/documents/transports/grpc.py +++ b/google/cloud/dialogflow_v2beta1/services/documents/transports/grpc.py @@ -61,6 +61,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -91,6 +92,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -107,6 +112,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -116,11 +126,6 @@ def __init__( 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 @@ -164,12 +169,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ @@ -343,6 +354,33 @@ def create_document( ) return self._stubs["create_document"] + @property + def import_documents( + self, + ) -> Callable[[document.ImportDocumentsRequest], operations.Operation]: + r"""Return a callable for the import documents method over gRPC. + + Create documents by importing data from external + sources. + + Returns: + Callable[[~.ImportDocumentsRequest], + ~.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 "import_documents" not in self._stubs: + self._stubs["import_documents"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Documents/ImportDocuments", + request_serializer=document.ImportDocumentsRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["import_documents"] + @property def delete_document( self, diff --git a/google/cloud/dialogflow_v2beta1/services/documents/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/documents/transports/grpc_asyncio.py index 0884bcba2..6e04daec1 100644 --- a/google/cloud/dialogflow_v2beta1/services/documents/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2beta1/services/documents/transports/grpc_asyncio.py @@ -105,6 +105,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -136,6 +137,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -152,6 +157,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -161,11 +171,6 @@ def __init__( 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 @@ -209,12 +214,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ @@ -353,6 +364,33 @@ def create_document( ) return self._stubs["create_document"] + @property + def import_documents( + self, + ) -> Callable[[document.ImportDocumentsRequest], Awaitable[operations.Operation]]: + r"""Return a callable for the import documents method over gRPC. + + Create documents by importing data from external + sources. + + Returns: + Callable[[~.ImportDocumentsRequest], + 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 "import_documents" not in self._stubs: + self._stubs["import_documents"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Documents/ImportDocuments", + request_serializer=document.ImportDocumentsRequest.serialize, + response_deserializer=operations.Operation.FromString, + ) + return self._stubs["import_documents"] + @property def delete_document( self, diff --git a/google/cloud/dialogflow_v2beta1/services/entity_types/async_client.py b/google/cloud/dialogflow_v2beta1/services/entity_types/async_client.py index 1322a0a25..8c323260b 100644 --- a/google/cloud/dialogflow_v2beta1/services/entity_types/async_client.py +++ b/google/cloud/dialogflow_v2beta1/services/entity_types/async_client.py @@ -80,7 +80,36 @@ class EntityTypesAsyncClient: EntityTypesClient.parse_common_location_path ) - from_service_account_file = EntityTypesClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EntityTypesAsyncClient: The constructed client. + """ + return EntityTypesClient.from_service_account_info.__func__(EntityTypesAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EntityTypesAsyncClient: The constructed client. + """ + return EntityTypesClient.from_service_account_file.__func__(EntityTypesAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -158,7 +187,7 @@ async def list_entity_types( agent. Args: - request (:class:`~.entity_type.ListEntityTypesRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.ListEntityTypesRequest`): The request object. The request message for [EntityTypes.ListEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.ListEntityTypes]. parent (:class:`str`): @@ -167,6 +196,7 @@ async def list_entity_types( - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -176,6 +206,7 @@ async def list_entity_types( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -187,7 +218,7 @@ async def list_entity_types( sent along with the request as metadata. Returns: - ~.pagers.ListEntityTypesAsyncPager: + google.cloud.dialogflow_v2beta1.services.entity_types.pagers.ListEntityTypesAsyncPager: The response message for [EntityTypes.ListEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.ListEntityTypes]. @@ -254,7 +285,7 @@ async def get_entity_type( r"""Retrieves the specified entity type. Args: - request (:class:`~.entity_type.GetEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.GetEntityTypeRequest`): The request object. The request message for [EntityTypes.GetEntityType][google.cloud.dialogflow.v2beta1.EntityTypes.GetEntityType]. name (:class:`str`): @@ -263,6 +294,7 @@ async def get_entity_type( - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -272,6 +304,7 @@ async def get_entity_type( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -283,22 +316,22 @@ async def get_entity_type( sent along with the request as metadata. Returns: - ~.entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2beta1.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -355,7 +388,7 @@ async def create_entity_type( r"""Creates an entity type in the specified agent. Args: - request (:class:`~.gcd_entity_type.CreateEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.CreateEntityTypeRequest`): The request object. The request message for [EntityTypes.CreateEntityType][google.cloud.dialogflow.v2beta1.EntityTypes.CreateEntityType]. parent (:class:`str`): @@ -364,10 +397,11 @@ async def create_entity_type( - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entity_type (:class:`~.gcd_entity_type.EntityType`): + entity_type (:class:`google.cloud.dialogflow_v2beta1.types.EntityType`): Required. The entity type to create. This corresponds to the ``entity_type`` field on the ``request`` instance; if ``request`` is provided, this @@ -378,6 +412,7 @@ async def create_entity_type( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -389,22 +424,22 @@ async def create_entity_type( sent along with the request as metadata. Returns: - ~.gcd_entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2beta1.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -463,10 +498,10 @@ async def update_entity_type( r"""Updates the specified entity type. Args: - request (:class:`~.gcd_entity_type.UpdateEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.UpdateEntityTypeRequest`): The request object. The request message for [EntityTypes.UpdateEntityType][google.cloud.dialogflow.v2beta1.EntityTypes.UpdateEntityType]. - entity_type (:class:`~.gcd_entity_type.EntityType`): + entity_type (:class:`google.cloud.dialogflow_v2beta1.types.EntityType`): Required. The entity type to update. This corresponds to the ``entity_type`` field on the ``request`` instance; if ``request`` is provided, this @@ -477,12 +512,14 @@ async def update_entity_type( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): Optional. 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. @@ -494,22 +531,22 @@ async def update_entity_type( sent along with the request as metadata. Returns: - ~.gcd_entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2beta1.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -568,7 +605,7 @@ async def delete_entity_type( r"""Deletes the specified entity type. Args: - request (:class:`~.entity_type.DeleteEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.DeleteEntityTypeRequest`): The request object. The request message for [EntityTypes.DeleteEntityType][google.cloud.dialogflow.v2beta1.EntityTypes.DeleteEntityType]. name (:class:`str`): @@ -577,6 +614,7 @@ async def delete_entity_type( - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -637,7 +675,7 @@ async def batch_update_entity_types( [BatchUpdateEntityTypesResponse][google.cloud.dialogflow.v2beta1.BatchUpdateEntityTypesResponse]> Args: - request (:class:`~.entity_type.BatchUpdateEntityTypesRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.BatchUpdateEntityTypesRequest`): The request object. The request message for [EntityTypes.BatchUpdateEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.BatchUpdateEntityTypes]. @@ -648,11 +686,11 @@ async def batch_update_entity_types( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.entity_type.BatchUpdateEntityTypesResponse``: + :class:`google.cloud.dialogflow_v2beta1.types.BatchUpdateEntityTypesResponse` The response message for [EntityTypes.BatchUpdateEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.BatchUpdateEntityTypes]. @@ -703,7 +741,7 @@ async def batch_delete_entity_types( Args: - request (:class:`~.entity_type.BatchDeleteEntityTypesRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.BatchDeleteEntityTypesRequest`): The request object. The request message for [EntityTypes.BatchDeleteEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.BatchDeleteEntityTypes]. parent (:class:`str`): @@ -712,12 +750,14 @@ async def batch_delete_entity_types( - ``projects//agent``, - ``projects//locations//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. entity_type_names (:class:`Sequence[str]`): Required. The names entity types to delete. All names must point to the same agent as ``parent``. + This corresponds to the ``entity_type_names`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -729,24 +769,22 @@ async def batch_delete_entity_types( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -815,7 +853,7 @@ async def batch_create_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchCreateEntitiesRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.BatchCreateEntitiesRequest`): The request object. The request message for [EntityTypes.BatchCreateEntities][google.cloud.dialogflow.v2beta1.EntityTypes.BatchCreateEntities]. parent (:class:`str`): @@ -824,10 +862,11 @@ async def batch_create_entities( - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entities (:class:`Sequence[~.entity_type.EntityType.Entity]`): + entities (:class:`Sequence[google.cloud.dialogflow_v2beta1.types.EntityType.Entity]`): Required. The entities to create. This corresponds to the ``entities`` field on the ``request`` instance; if ``request`` is provided, this @@ -838,6 +877,7 @@ async def batch_create_entities( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -849,24 +889,22 @@ async def batch_create_entities( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -939,7 +977,7 @@ async def batch_update_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchUpdateEntitiesRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.BatchUpdateEntitiesRequest`): The request object. The request message for [EntityTypes.BatchUpdateEntities][google.cloud.dialogflow.v2beta1.EntityTypes.BatchUpdateEntities]. parent (:class:`str`): @@ -948,12 +986,14 @@ async def batch_update_entities( - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entities (:class:`Sequence[~.entity_type.EntityType.Entity]`): + entities (:class:`Sequence[google.cloud.dialogflow_v2beta1.types.EntityType.Entity]`): Required. The entities to update or create. + This corresponds to the ``entities`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -963,6 +1003,7 @@ async def batch_update_entities( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -974,24 +1015,22 @@ async def batch_update_entities( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1062,7 +1101,7 @@ async def batch_delete_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchDeleteEntitiesRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.BatchDeleteEntitiesRequest`): The request object. The request message for [EntityTypes.BatchDeleteEntities][google.cloud.dialogflow.v2beta1.EntityTypes.BatchDeleteEntities]. parent (:class:`str`): @@ -1071,6 +1110,7 @@ async def batch_delete_entities( - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1078,6 +1118,7 @@ async def batch_delete_entities( Required. The reference ``values`` of the entities to delete. Note that these are not fully-qualified names, i.e. they don't start with ``projects/``. + This corresponds to the ``entity_values`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1087,6 +1128,7 @@ async def batch_delete_entities( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1098,24 +1140,22 @@ async def batch_delete_entities( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. diff --git a/google/cloud/dialogflow_v2beta1/services/entity_types/client.py b/google/cloud/dialogflow_v2beta1/services/entity_types/client.py index f5c4890e7..32b206474 100644 --- a/google/cloud/dialogflow_v2beta1/services/entity_types/client.py +++ b/google/cloud/dialogflow_v2beta1/services/entity_types/client.py @@ -116,6 +116,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EntityTypesClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -128,7 +144,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + EntityTypesClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -235,10 +251,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.EntityTypesTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -274,21 +290,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -331,7 +343,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -350,24 +362,26 @@ def list_entity_types( agent. Args: - request (:class:`~.entity_type.ListEntityTypesRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListEntityTypesRequest): The request object. The request message for [EntityTypes.ListEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.ListEntityTypes]. - parent (:class:`str`): + parent (str): Required. The agent to list all entity types from. Supported formats: - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -379,7 +393,7 @@ def list_entity_types( sent along with the request as metadata. Returns: - ~.pagers.ListEntityTypesPager: + google.cloud.dialogflow_v2beta1.services.entity_types.pagers.ListEntityTypesPager: The response message for [EntityTypes.ListEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.ListEntityTypes]. @@ -447,24 +461,26 @@ def get_entity_type( r"""Retrieves the specified entity type. Args: - request (:class:`~.entity_type.GetEntityTypeRequest`): + request (google.cloud.dialogflow_v2beta1.types.GetEntityTypeRequest): The request object. The request message for [EntityTypes.GetEntityType][google.cloud.dialogflow.v2beta1.EntityTypes.GetEntityType]. - name (:class:`str`): + name (str): Required. The name of the entity type. Supported formats: - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -476,22 +492,22 @@ def get_entity_type( sent along with the request as metadata. Returns: - ~.entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2beta1.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -549,29 +565,31 @@ def create_entity_type( r"""Creates an entity type in the specified agent. Args: - request (:class:`~.gcd_entity_type.CreateEntityTypeRequest`): + request (google.cloud.dialogflow_v2beta1.types.CreateEntityTypeRequest): The request object. The request message for [EntityTypes.CreateEntityType][google.cloud.dialogflow.v2beta1.EntityTypes.CreateEntityType]. - parent (:class:`str`): + parent (str): Required. The agent to create a entity type for. Supported formats: - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entity_type (:class:`~.gcd_entity_type.EntityType`): + entity_type (google.cloud.dialogflow_v2beta1.types.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. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -583,22 +601,22 @@ def create_entity_type( sent along with the request as metadata. Returns: - ~.gcd_entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2beta1.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -658,26 +676,28 @@ def update_entity_type( r"""Updates the specified entity type. Args: - request (:class:`~.gcd_entity_type.UpdateEntityTypeRequest`): + request (google.cloud.dialogflow_v2beta1.types.UpdateEntityTypeRequest): The request object. The request message for [EntityTypes.UpdateEntityType][google.cloud.dialogflow.v2beta1.EntityTypes.UpdateEntityType]. - entity_type (:class:`~.gcd_entity_type.EntityType`): + entity_type (google.cloud.dialogflow_v2beta1.types.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. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. 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. @@ -689,22 +709,22 @@ def update_entity_type( sent along with the request as metadata. Returns: - ~.gcd_entity_type.EntityType: - Each intent parameter has a type, called the entity - type, which dictates exactly how data from an end-user - expression is extracted. - - Dialogflow provides predefined system entities that can - match many common types of data. For example, there are - system entities for matching dates, times, colors, email - addresses, and so on. You can also create your own - custom entities for matching custom data. For example, - you could define a vegetable entity that can match the - types of vegetables available for purchase with a - grocery store agent. - - For more information, see the `Entity - guide `__. + google.cloud.dialogflow_v2beta1.types.EntityType: + Each intent parameter has a type, called the entity type, which dictates + exactly how data from an end-user expression is + extracted. + + Dialogflow provides predefined system entities that + can match many common types of data. For example, + there are system entities for matching dates, times, + colors, email addresses, and so on. You can also + create your own custom entities for matching custom + data. For example, you could define a vegetable + entity that can match the types of vegetables + available for purchase with a grocery store agent. + + For more information, see the [Entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-overview). """ # Create or coerce a protobuf request object. @@ -764,15 +784,16 @@ def delete_entity_type( r"""Deletes the specified entity type. Args: - request (:class:`~.entity_type.DeleteEntityTypeRequest`): + request (google.cloud.dialogflow_v2beta1.types.DeleteEntityTypeRequest): The request object. The request message for [EntityTypes.DeleteEntityType][google.cloud.dialogflow.v2beta1.EntityTypes.DeleteEntityType]. - name (:class:`str`): + name (str): Required. The name of the entity type to delete. Supported formats: - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -834,7 +855,7 @@ def batch_update_entity_types( [BatchUpdateEntityTypesResponse][google.cloud.dialogflow.v2beta1.BatchUpdateEntityTypesResponse]> Args: - request (:class:`~.entity_type.BatchUpdateEntityTypesRequest`): + request (google.cloud.dialogflow_v2beta1.types.BatchUpdateEntityTypesRequest): The request object. The request message for [EntityTypes.BatchUpdateEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.BatchUpdateEntityTypes]. @@ -845,11 +866,11 @@ def batch_update_entity_types( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.entity_type.BatchUpdateEntityTypesResponse``: + :class:`google.cloud.dialogflow_v2beta1.types.BatchUpdateEntityTypesResponse` The response message for [EntityTypes.BatchUpdateEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.BatchUpdateEntityTypes]. @@ -903,21 +924,23 @@ def batch_delete_entity_types( Args: - request (:class:`~.entity_type.BatchDeleteEntityTypesRequest`): + request (google.cloud.dialogflow_v2beta1.types.BatchDeleteEntityTypesRequest): The request object. The request message for [EntityTypes.BatchDeleteEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.BatchDeleteEntityTypes]. - parent (:class:`str`): + parent (str): Required. The name of the agent to delete all entities types for. Supported formats: - ``projects//agent``, - ``projects//locations//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entity_type_names (:class:`Sequence[str]`): + entity_type_names (Sequence[str]): Required. The names entity types to delete. All names must point to the same agent as ``parent``. + This corresponds to the ``entity_type_names`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -929,24 +952,22 @@ def batch_delete_entity_types( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -971,9 +992,8 @@ def batch_delete_entity_types( if parent is not None: request.parent = parent - - if entity_type_names: - request.entity_type_names.extend(entity_type_names) + if entity_type_names is not None: + request.entity_type_names = entity_type_names # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. @@ -1018,29 +1038,31 @@ def batch_create_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchCreateEntitiesRequest`): + request (google.cloud.dialogflow_v2beta1.types.BatchCreateEntitiesRequest): The request object. The request message for [EntityTypes.BatchCreateEntities][google.cloud.dialogflow.v2beta1.EntityTypes.BatchCreateEntities]. - parent (:class:`str`): + parent (str): Required. The name of the entity type to create entities in. Supported formats: - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entities (:class:`Sequence[~.entity_type.EntityType.Entity]`): + entities (Sequence[google.cloud.dialogflow_v2beta1.types.EntityType.Entity]): Required. The entities to create. This corresponds to the ``entities`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1052,24 +1074,22 @@ def batch_create_entities( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1094,12 +1114,11 @@ def batch_create_entities( if parent is not None: request.parent = parent + if entities is not None: + request.entities = entities if language_code is not None: request.language_code = language_code - if entities: - request.entities.extend(entities) - # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. rpc = self._transport._wrapped_methods[self._transport.batch_create_entities] @@ -1143,30 +1162,33 @@ def batch_update_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchUpdateEntitiesRequest`): + request (google.cloud.dialogflow_v2beta1.types.BatchUpdateEntitiesRequest): The request object. The request message for [EntityTypes.BatchUpdateEntities][google.cloud.dialogflow.v2beta1.EntityTypes.BatchUpdateEntities]. - parent (:class:`str`): + parent (str): Required. The name of the entity type to update or create entities in. Supported formats: - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entities (:class:`Sequence[~.entity_type.EntityType.Entity]`): + entities (Sequence[google.cloud.dialogflow_v2beta1.types.EntityType.Entity]): Required. The entities to update or create. + This corresponds to the ``entities`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1178,24 +1200,22 @@ def batch_update_entities( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1220,12 +1240,11 @@ def batch_update_entities( if parent is not None: request.parent = parent + if entities is not None: + request.entities = entities if language_code is not None: request.language_code = language_code - if entities: - request.entities.extend(entities) - # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. rpc = self._transport._wrapped_methods[self._transport.batch_update_entities] @@ -1267,31 +1286,34 @@ def batch_delete_entities( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.entity_type.BatchDeleteEntitiesRequest`): + request (google.cloud.dialogflow_v2beta1.types.BatchDeleteEntitiesRequest): The request object. The request message for [EntityTypes.BatchDeleteEntities][google.cloud.dialogflow.v2beta1.EntityTypes.BatchDeleteEntities]. - parent (:class:`str`): + parent (str): Required. The name of the entity type to delete entries for. Supported formats: - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - entity_values (:class:`Sequence[str]`): + entity_values (Sequence[str]): Required. The reference ``values`` of the entities to delete. Note that these are not fully-qualified names, i.e. they don't start with ``projects/``. + This corresponds to the ``entity_values`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -1303,24 +1325,22 @@ def batch_delete_entities( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1345,12 +1365,11 @@ def batch_delete_entities( if parent is not None: request.parent = parent + if entity_values is not None: + request.entity_values = entity_values if language_code is not None: request.language_code = language_code - if entity_values: - request.entity_values.extend(entity_values) - # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. rpc = self._transport._wrapped_methods[self._transport.batch_delete_entities] diff --git a/google/cloud/dialogflow_v2beta1/services/entity_types/pagers.py b/google/cloud/dialogflow_v2beta1/services/entity_types/pagers.py index 0efe08d05..06d3999b4 100644 --- a/google/cloud/dialogflow_v2beta1/services/entity_types/pagers.py +++ b/google/cloud/dialogflow_v2beta1/services/entity_types/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2beta1.types import entity_type @@ -24,7 +33,7 @@ class ListEntityTypesPager: """A pager for iterating through ``list_entity_types`` requests. This class thinly wraps an initial - :class:`~.entity_type.ListEntityTypesResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListEntityTypesResponse` object, and provides an ``__iter__`` method to iterate through its ``entity_types`` field. @@ -33,7 +42,7 @@ class ListEntityTypesPager: through the ``entity_types`` field on the corresponding responses. - All the usual :class:`~.entity_type.ListEntityTypesResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.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. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.entity_type.ListEntityTypesRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListEntityTypesRequest): The initial request object. - response (:class:`~.entity_type.ListEntityTypesResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListEntityTypesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListEntityTypesAsyncPager: """A pager for iterating through ``list_entity_types`` requests. This class thinly wraps an initial - :class:`~.entity_type.ListEntityTypesResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListEntityTypesResponse` object, and provides an ``__aiter__`` method to iterate through its ``entity_types`` field. @@ -95,7 +104,7 @@ class ListEntityTypesAsyncPager: through the ``entity_types`` field on the corresponding responses. - All the usual :class:`~.entity_type.ListEntityTypesResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.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. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.entity_type.ListEntityTypesRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListEntityTypesRequest): The initial request object. - response (:class:`~.entity_type.ListEntityTypesResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListEntityTypesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2beta1/services/entity_types/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/entity_types/transports/grpc.py index 089cf74f4..eb180c61f 100644 --- a/google/cloud/dialogflow_v2beta1/services/entity_types/transports/grpc.py +++ b/google/cloud/dialogflow_v2beta1/services/entity_types/transports/grpc.py @@ -62,6 +62,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -92,6 +93,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -108,6 +113,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -117,11 +127,6 @@ def __init__( 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 @@ -165,12 +170,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/entity_types/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/entity_types/transports/grpc_asyncio.py index 371adc9d5..53985db1c 100644 --- a/google/cloud/dialogflow_v2beta1/services/entity_types/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2beta1/services/entity_types/transports/grpc_asyncio.py @@ -106,6 +106,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -137,6 +138,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -153,6 +158,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -162,11 +172,6 @@ def __init__( 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 @@ -210,12 +215,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/environments/async_client.py b/google/cloud/dialogflow_v2beta1/services/environments/async_client.py index 4b794f4ef..232e39ebf 100644 --- a/google/cloud/dialogflow_v2beta1/services/environments/async_client.py +++ b/google/cloud/dialogflow_v2beta1/services/environments/async_client.py @@ -74,7 +74,36 @@ class EnvironmentsAsyncClient: EnvironmentsClient.parse_common_location_path ) - from_service_account_file = EnvironmentsClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EnvironmentsAsyncClient: The constructed client. + """ + return EnvironmentsClient.from_service_account_info.__func__(EnvironmentsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EnvironmentsAsyncClient: The constructed client. + """ + return EnvironmentsClient.from_service_account_file.__func__(EnvironmentsAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -151,7 +180,7 @@ async def list_environments( specified agent. Args: - request (:class:`~.environment.ListEnvironmentsRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.ListEnvironmentsRequest`): The request object. The request message for [Environments.ListEnvironments][google.cloud.dialogflow.v2beta1.Environments.ListEnvironments]. parent (:class:`str`): @@ -160,6 +189,7 @@ async def list_environments( - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -171,7 +201,7 @@ async def list_environments( sent along with the request as metadata. Returns: - ~.pagers.ListEnvironmentsAsyncPager: + google.cloud.dialogflow_v2beta1.services.environments.pagers.ListEnvironmentsAsyncPager: The response message for [Environments.ListEnvironments][google.cloud.dialogflow.v2beta1.Environments.ListEnvironments]. diff --git a/google/cloud/dialogflow_v2beta1/services/environments/client.py b/google/cloud/dialogflow_v2beta1/services/environments/client.py index 7e6dc33ed..a9e38d254 100644 --- a/google/cloud/dialogflow_v2beta1/services/environments/client.py +++ b/google/cloud/dialogflow_v2beta1/services/environments/client.py @@ -110,6 +110,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + EnvironmentsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -122,7 +138,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + EnvironmentsClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -229,10 +245,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.EnvironmentsTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -268,21 +284,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -325,7 +337,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -343,15 +355,16 @@ def list_environments( specified agent. Args: - request (:class:`~.environment.ListEnvironmentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListEnvironmentsRequest): The request object. The request message for [Environments.ListEnvironments][google.cloud.dialogflow.v2beta1.Environments.ListEnvironments]. - parent (:class:`str`): + parent (str): Required. The agent to list all environments from. Format: - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -363,7 +376,7 @@ def list_environments( sent along with the request as metadata. Returns: - ~.pagers.ListEnvironmentsPager: + google.cloud.dialogflow_v2beta1.services.environments.pagers.ListEnvironmentsPager: The response message for [Environments.ListEnvironments][google.cloud.dialogflow.v2beta1.Environments.ListEnvironments]. diff --git a/google/cloud/dialogflow_v2beta1/services/environments/pagers.py b/google/cloud/dialogflow_v2beta1/services/environments/pagers.py index d366c65f8..c46469235 100644 --- a/google/cloud/dialogflow_v2beta1/services/environments/pagers.py +++ b/google/cloud/dialogflow_v2beta1/services/environments/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2beta1.types import environment @@ -24,7 +33,7 @@ class ListEnvironmentsPager: """A pager for iterating through ``list_environments`` requests. This class thinly wraps an initial - :class:`~.environment.ListEnvironmentsResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListEnvironmentsResponse` object, and provides an ``__iter__`` method to iterate through its ``environments`` field. @@ -33,7 +42,7 @@ class ListEnvironmentsPager: through the ``environments`` field on the corresponding responses. - All the usual :class:`~.environment.ListEnvironmentsResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.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. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.environment.ListEnvironmentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListEnvironmentsRequest): The initial request object. - response (:class:`~.environment.ListEnvironmentsResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListEnvironmentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListEnvironmentsAsyncPager: """A pager for iterating through ``list_environments`` requests. This class thinly wraps an initial - :class:`~.environment.ListEnvironmentsResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListEnvironmentsResponse` object, and provides an ``__aiter__`` method to iterate through its ``environments`` field. @@ -95,7 +104,7 @@ class ListEnvironmentsAsyncPager: through the ``environments`` field on the corresponding responses. - All the usual :class:`~.environment.ListEnvironmentsResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.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. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.environment.ListEnvironmentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListEnvironmentsRequest): The initial request object. - response (:class:`~.environment.ListEnvironmentsResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListEnvironmentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2beta1/services/environments/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/environments/transports/grpc.py index ede48c0b9..de5505597 100644 --- a/google/cloud/dialogflow_v2beta1/services/environments/transports/grpc.py +++ b/google/cloud/dialogflow_v2beta1/services/environments/transports/grpc.py @@ -58,6 +58,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -88,6 +89,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -104,6 +109,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -113,11 +123,6 @@ def __init__( 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 @@ -161,12 +166,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/environments/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/environments/transports/grpc_asyncio.py index 71e613ff1..bce9fca5b 100644 --- a/google/cloud/dialogflow_v2beta1/services/environments/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2beta1/services/environments/transports/grpc_asyncio.py @@ -102,6 +102,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -133,6 +134,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -149,6 +154,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -158,11 +168,6 @@ def __init__( 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 @@ -206,12 +211,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/intents/async_client.py b/google/cloud/dialogflow_v2beta1/services/intents/async_client.py index cf947f90a..023b4d49d 100644 --- a/google/cloud/dialogflow_v2beta1/services/intents/async_client.py +++ b/google/cloud/dialogflow_v2beta1/services/intents/async_client.py @@ -79,7 +79,36 @@ class IntentsAsyncClient: 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 + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + IntentsAsyncClient: The constructed client. + """ + return IntentsClient.from_service_account_info.__func__(IntentsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + IntentsAsyncClient: The constructed client. + """ + return IntentsClient.from_service_account_file.__func__(IntentsAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -157,12 +186,13 @@ async def list_intents( agent. Args: - request (:class:`~.intent.ListIntentsRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.ListIntentsRequest`): The request object. The request message for [Intents.ListIntents][google.cloud.dialogflow.v2beta1.Intents.ListIntents]. parent (:class:`str`): Required. The agent to list all intents from. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -172,6 +202,7 @@ async def list_intents( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -183,7 +214,7 @@ async def list_intents( sent along with the request as metadata. Returns: - ~.pagers.ListIntentsAsyncPager: + google.cloud.dialogflow_v2beta1.services.intents.pagers.ListIntentsAsyncPager: The response message for [Intents.ListIntents][google.cloud.dialogflow.v2beta1.Intents.ListIntents]. @@ -250,7 +281,7 @@ async def get_intent( r"""Retrieves the specified intent. Args: - request (:class:`~.intent.GetIntentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.GetIntentRequest`): The request object. The request message for [Intents.GetIntent][google.cloud.dialogflow.v2beta1.Intents.GetIntent]. name (:class:`str`): @@ -258,6 +289,7 @@ async def get_intent( - ``projects//agent/intents/`` - ``projects//locations//agent/intents/`` + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -267,6 +299,7 @@ async def get_intent( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -278,18 +311,18 @@ async def get_intent( sent along with the request as metadata. Returns: - ~.intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2beta1.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -346,7 +379,7 @@ async def create_intent( r"""Creates an intent in the specified agent. Args: - request (:class:`~.gcd_intent.CreateIntentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.CreateIntentRequest`): The request object. The request message for [Intents.CreateIntent][google.cloud.dialogflow.v2beta1.Intents.CreateIntent]. parent (:class:`str`): @@ -355,10 +388,11 @@ async def create_intent( - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intent (:class:`~.gcd_intent.Intent`): + intent (:class:`google.cloud.dialogflow_v2beta1.types.Intent`): Required. The intent to create. This corresponds to the ``intent`` field on the ``request`` instance; if ``request`` is provided, this @@ -369,6 +403,7 @@ async def create_intent( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -380,18 +415,18 @@ async def create_intent( sent along with the request as metadata. Returns: - ~.gcd_intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2beta1.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -450,17 +485,18 @@ async def update_intent( r"""Updates the specified intent. Args: - request (:class:`~.gcd_intent.UpdateIntentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.UpdateIntentRequest`): The request object. The request message for [Intents.UpdateIntent][google.cloud.dialogflow.v2beta1.Intents.UpdateIntent]. - intent (:class:`~.gcd_intent.Intent`): + intent (:class:`google.cloud.dialogflow_v2beta1.types.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`): + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): Optional. 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. @@ -470,6 +506,7 @@ async def update_intent( used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -481,18 +518,18 @@ async def update_intent( sent along with the request as metadata. Returns: - ~.gcd_intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2beta1.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -552,7 +589,7 @@ async def delete_intent( indirect followup intents. Args: - request (:class:`~.intent.DeleteIntentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.DeleteIntentRequest`): The request object. The request message for [Intents.DeleteIntent][google.cloud.dialogflow.v2beta1.Intents.DeleteIntent]. name (:class:`str`): @@ -564,6 +601,7 @@ async def delete_intent( - ``projects//agent/intents/`` - ``projects//locations//agent/intents/`` + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -628,7 +666,7 @@ async def batch_update_intents( [BatchUpdateIntentsResponse][google.cloud.dialogflow.v2beta1.BatchUpdateIntentsResponse]> Args: - request (:class:`~.intent.BatchUpdateIntentsRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.BatchUpdateIntentsRequest`): The request object. The request message for [Intents.BatchUpdateIntents][google.cloud.dialogflow.v2beta1.Intents.BatchUpdateIntents]. parent (:class:`str`): @@ -637,6 +675,7 @@ async def batch_update_intents( - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -647,12 +686,14 @@ async def batch_update_intents( serialized proto (of IntentBatch type) or JSON object. Note: The URI must start with "gs://". + This corresponds to the ``intent_batch_uri`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intent_batch_inline (:class:`~.intent.IntentBatch`): + intent_batch_inline (:class:`google.cloud.dialogflow_v2beta1.types.IntentBatch`): The collection of intents to update or create. + This corresponds to the ``intent_batch_inline`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -664,12 +705,12 @@ async def batch_update_intents( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.operation_async.AsyncOperation: An object representing a long-running operation. The result type for the operation will be - :class:``~.intent.BatchUpdateIntentsResponse``: The - response message for + :class:`google.cloud.dialogflow_v2beta1.types.BatchUpdateIntentsResponse` + The response message for [Intents.BatchUpdateIntents][google.cloud.dialogflow.v2beta1.Intents.BatchUpdateIntents]. """ @@ -739,7 +780,7 @@ async def batch_delete_intents( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.intent.BatchDeleteIntentsRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.BatchDeleteIntentsRequest`): The request object. The request message for [Intents.BatchDeleteIntents][google.cloud.dialogflow.v2beta1.Intents.BatchDeleteIntents]. parent (:class:`str`): @@ -748,12 +789,14 @@ async def batch_delete_intents( - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intents (:class:`Sequence[~.intent.Intent]`): + intents (:class:`Sequence[google.cloud.dialogflow_v2beta1.types.Intent]`): Required. The collection of intents to delete. Only intent ``name`` must be filled in. + This corresponds to the ``intents`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -765,24 +808,22 @@ async def batch_delete_intents( sent along with the request as metadata. Returns: - ~.operation_async.AsyncOperation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. diff --git a/google/cloud/dialogflow_v2beta1/services/intents/client.py b/google/cloud/dialogflow_v2beta1/services/intents/client.py index 5c5ff03f2..392e2d270 100644 --- a/google/cloud/dialogflow_v2beta1/services/intents/client.py +++ b/google/cloud/dialogflow_v2beta1/services/intents/client.py @@ -117,6 +117,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + IntentsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -129,7 +145,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + IntentsClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -250,10 +266,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.IntentsTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -289,21 +305,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -346,7 +358,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -365,21 +377,23 @@ def list_intents( agent. Args: - request (:class:`~.intent.ListIntentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListIntentsRequest): The request object. The request message for [Intents.ListIntents][google.cloud.dialogflow.v2beta1.Intents.ListIntents]. - parent (:class:`str`): + parent (str): Required. The agent to list all intents from. Format: ``projects//agent``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -391,7 +405,7 @@ def list_intents( sent along with the request as metadata. Returns: - ~.pagers.ListIntentsPager: + google.cloud.dialogflow_v2beta1.services.intents.pagers.ListIntentsPager: The response message for [Intents.ListIntents][google.cloud.dialogflow.v2beta1.Intents.ListIntents]. @@ -459,23 +473,25 @@ def get_intent( r"""Retrieves the specified intent. Args: - request (:class:`~.intent.GetIntentRequest`): + request (google.cloud.dialogflow_v2beta1.types.GetIntentRequest): The request object. The request message for [Intents.GetIntent][google.cloud.dialogflow.v2beta1.Intents.GetIntent]. - name (:class:`str`): + name (str): Required. The name of the intent. Supported formats: - ``projects//agent/intents/`` - ``projects//locations//agent/intents/`` + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -487,18 +503,18 @@ def get_intent( sent along with the request as metadata. Returns: - ~.intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2beta1.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -556,29 +572,31 @@ def create_intent( r"""Creates an intent in the specified agent. Args: - request (:class:`~.gcd_intent.CreateIntentRequest`): + request (google.cloud.dialogflow_v2beta1.types.CreateIntentRequest): The request object. The request message for [Intents.CreateIntent][google.cloud.dialogflow.v2beta1.Intents.CreateIntent]. - parent (:class:`str`): + parent (str): Required. The agent to create a intent for. Supported formats: - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intent (:class:`~.gcd_intent.Intent`): + intent (google.cloud.dialogflow_v2beta1.types.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. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -590,18 +608,18 @@ def create_intent( sent along with the request as metadata. Returns: - ~.gcd_intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2beta1.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -661,26 +679,28 @@ def update_intent( r"""Updates the specified intent. Args: - request (:class:`~.gcd_intent.UpdateIntentRequest`): + request (google.cloud.dialogflow_v2beta1.types.UpdateIntentRequest): The request object. The request message for [Intents.UpdateIntent][google.cloud.dialogflow.v2beta1.Intents.UpdateIntent]. - intent (:class:`~.gcd_intent.Intent`): + intent (google.cloud.dialogflow_v2beta1.types.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`): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. 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. - language_code (:class:`str`): + language_code (str): Optional. The language used to access language-specific data. If not specified, the agent's default language is used. For more information, see `Multilingual intent and entity data `__. + This corresponds to the ``language_code`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -692,18 +712,18 @@ def update_intent( sent along with the request as metadata. Returns: - ~.gcd_intent.Intent: - An intent categorizes an end-user's intention for one - conversation turn. For each agent, you define many - intents, where your combined intents can handle a - complete conversation. When an end-user writes or says - something, referred to as an end-user expression or - end-user input, Dialogflow matches the end-user input to - the best intent in your agent. Matching an intent is - also known as intent classification. - - For more information, see the `intent - guide `__. + google.cloud.dialogflow_v2beta1.types.Intent: + An intent categorizes an end-user's intention for one conversation turn. For + each agent, you define many intents, where your + combined intents can handle a complete conversation. + When an end-user writes or says something, referred + to as an end-user expression or end-user input, + Dialogflow matches the end-user input to the best + intent in your agent. Matching an intent is also + known as intent classification. + + For more information, see the [intent + guide](\ https://cloud.google.com/dialogflow/docs/intents-overview). """ # Create or coerce a protobuf request object. @@ -764,10 +784,10 @@ def delete_intent( indirect followup intents. Args: - request (:class:`~.intent.DeleteIntentRequest`): + request (google.cloud.dialogflow_v2beta1.types.DeleteIntentRequest): The request object. The request message for [Intents.DeleteIntent][google.cloud.dialogflow.v2beta1.Intents.DeleteIntent]. - name (:class:`str`): + name (str): Required. The name of the intent to delete. If this intent has direct or indirect followup intents, we also delete them. @@ -776,6 +796,7 @@ def delete_intent( - ``projects//agent/intents/`` - ``projects//locations//agent/intents/`` + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -841,31 +862,34 @@ def batch_update_intents( [BatchUpdateIntentsResponse][google.cloud.dialogflow.v2beta1.BatchUpdateIntentsResponse]> Args: - request (:class:`~.intent.BatchUpdateIntentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.BatchUpdateIntentsRequest): The request object. The request message for [Intents.BatchUpdateIntents][google.cloud.dialogflow.v2beta1.Intents.BatchUpdateIntents]. - parent (:class:`str`): + parent (str): Required. The name of the agent to update or create intents in. Supported formats: - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intent_batch_uri (:class:`str`): + intent_batch_uri (str): The URI to a Google Cloud Storage file containing intents to update or create. The file format can either be a serialized proto (of IntentBatch type) or JSON object. Note: The URI must start with "gs://". + This corresponds to the ``intent_batch_uri`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intent_batch_inline (:class:`~.intent.IntentBatch`): + intent_batch_inline (google.cloud.dialogflow_v2beta1.types.IntentBatch): The collection of intents to update or create. + This corresponds to the ``intent_batch_inline`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -877,12 +901,12 @@ def batch_update_intents( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.operation.Operation: An object representing a long-running operation. The result type for the operation will be - :class:``~.intent.BatchUpdateIntentsResponse``: The - response message for + :class:`google.cloud.dialogflow_v2beta1.types.BatchUpdateIntentsResponse` + The response message for [Intents.BatchUpdateIntents][google.cloud.dialogflow.v2beta1.Intents.BatchUpdateIntents]. """ @@ -953,21 +977,23 @@ def batch_delete_intents( [google.protobuf.Empty][google.protobuf.Empty]> Args: - request (:class:`~.intent.BatchDeleteIntentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.BatchDeleteIntentsRequest): The request object. The request message for [Intents.BatchDeleteIntents][google.cloud.dialogflow.v2beta1.Intents.BatchDeleteIntents]. - parent (:class:`str`): + parent (str): Required. The name of the agent to delete all entities types for. Supported formats: - ``projects//agent`` - ``projects//locations//agent`` + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - intents (:class:`Sequence[~.intent.Intent]`): + intents (Sequence[google.cloud.dialogflow_v2beta1.types.Intent]): Required. The collection of intents to delete. Only intent ``name`` must be filled in. + This corresponds to the ``intents`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -979,24 +1005,22 @@ def batch_delete_intents( sent along with the request as metadata. Returns: - ~.operation.Operation: + google.api_core.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: + The result type for the operation will be :class:`google.protobuf.empty_pb2.Empty` A generic empty message that you can re-use to avoid defining duplicated + empty messages in your APIs. A typical example is to + use it as the request or the response type of an API + method. For instance: - :: + service Foo { + rpc Bar(google.protobuf.Empty) returns + (google.protobuf.Empty); - service Foo { - rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); - } + } - The JSON representation for ``Empty`` is empty JSON - object ``{}``. + The JSON representation for Empty is empty JSON + object {}. """ # Create or coerce a protobuf request object. @@ -1021,9 +1045,8 @@ def batch_delete_intents( if parent is not None: request.parent = parent - - if intents: - request.intents.extend(intents) + if intents is not None: + request.intents = intents # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. diff --git a/google/cloud/dialogflow_v2beta1/services/intents/pagers.py b/google/cloud/dialogflow_v2beta1/services/intents/pagers.py index 6203ee246..7a8547bd6 100644 --- a/google/cloud/dialogflow_v2beta1/services/intents/pagers.py +++ b/google/cloud/dialogflow_v2beta1/services/intents/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2beta1.types import intent @@ -24,7 +33,7 @@ class ListIntentsPager: """A pager for iterating through ``list_intents`` requests. This class thinly wraps an initial - :class:`~.intent.ListIntentsResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListIntentsResponse` object, and provides an ``__iter__`` method to iterate through its ``intents`` field. @@ -33,7 +42,7 @@ class ListIntentsPager: through the ``intents`` field on the corresponding responses. - All the usual :class:`~.intent.ListIntentsResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.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. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.intent.ListIntentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListIntentsRequest): The initial request object. - response (:class:`~.intent.ListIntentsResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListIntentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListIntentsAsyncPager: """A pager for iterating through ``list_intents`` requests. This class thinly wraps an initial - :class:`~.intent.ListIntentsResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListIntentsResponse` object, and provides an ``__aiter__`` method to iterate through its ``intents`` field. @@ -95,7 +104,7 @@ class ListIntentsAsyncPager: through the ``intents`` field on the corresponding responses. - All the usual :class:`~.intent.ListIntentsResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.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. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.intent.ListIntentsRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListIntentsRequest): The initial request object. - response (:class:`~.intent.ListIntentsResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListIntentsResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2beta1/services/intents/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/intents/transports/grpc.py index 81f14b65f..c9d7893e6 100644 --- a/google/cloud/dialogflow_v2beta1/services/intents/transports/grpc.py +++ b/google/cloud/dialogflow_v2beta1/services/intents/transports/grpc.py @@ -62,6 +62,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -92,6 +93,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -108,6 +113,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -117,11 +127,6 @@ def __init__( 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 @@ -165,12 +170,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/intents/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/intents/transports/grpc_asyncio.py index d3e7aff5b..e24b339d6 100644 --- a/google/cloud/dialogflow_v2beta1/services/intents/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2beta1/services/intents/transports/grpc_asyncio.py @@ -106,6 +106,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -137,6 +138,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -153,6 +158,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -162,11 +172,6 @@ def __init__( 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 @@ -210,12 +215,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/knowledge_bases/async_client.py b/google/cloud/dialogflow_v2beta1/services/knowledge_bases/async_client.py index 350a0353f..dfefff910 100644 --- a/google/cloud/dialogflow_v2beta1/services/knowledge_bases/async_client.py +++ b/google/cloud/dialogflow_v2beta1/services/knowledge_bases/async_client.py @@ -82,7 +82,36 @@ class KnowledgeBasesAsyncClient: KnowledgeBasesClient.parse_common_location_path ) - from_service_account_file = KnowledgeBasesClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + KnowledgeBasesAsyncClient: The constructed client. + """ + return KnowledgeBasesClient.from_service_account_info.__func__(KnowledgeBasesAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + KnowledgeBasesAsyncClient: The constructed client. + """ + return KnowledgeBasesClient.from_service_account_file.__func__(KnowledgeBasesAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -161,13 +190,14 @@ async def list_knowledge_bases( deprecated; only use ``projects.knowledgeBases``. Args: - request (:class:`~.knowledge_base.ListKnowledgeBasesRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.ListKnowledgeBasesRequest`): The request object. Request message for [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2beta1.KnowledgeBases.ListKnowledgeBases]. parent (:class:`str`): Required. The project to list of knowledge bases for. Format: ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -179,7 +209,7 @@ async def list_knowledge_bases( sent along with the request as metadata. Returns: - ~.pagers.ListKnowledgeBasesAsyncPager: + google.cloud.dialogflow_v2beta1.services.knowledge_bases.pagers.ListKnowledgeBasesAsyncPager: Response message for [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2beta1.KnowledgeBases.ListKnowledgeBases]. @@ -246,13 +276,14 @@ async def get_knowledge_base( deprecated; only use ``projects.knowledgeBases``. Args: - request (:class:`~.knowledge_base.GetKnowledgeBaseRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.GetKnowledgeBaseRequest`): The request object. Request message for [KnowledgeBases.GetKnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBases.GetKnowledgeBase]. name (:class:`str`): Required. The name of the knowledge base to retrieve. Format ``projects//locations//knowledgeBases/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -264,19 +295,19 @@ async def get_knowledge_base( sent along with the request as metadata. Returns: - ~.knowledge_base.KnowledgeBase: - A knowledge base represents a collection of knowledge - documents that you provide to Dialogflow. Your knowledge - documents contain information that may be useful during - conversations with end-users. Some Dialogflow features - use knowledge bases when looking for a response to an - end-user input. + google.cloud.dialogflow_v2beta1.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases`` resource is - deprecated; only use ``projects.knowledgeBases``. + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. """ # Create or coerce a protobuf request object. @@ -333,19 +364,21 @@ async def create_knowledge_base( deprecated; only use ``projects.knowledgeBases``. Args: - request (:class:`~.gcd_knowledge_base.CreateKnowledgeBaseRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.CreateKnowledgeBaseRequest`): The request object. Request message for [KnowledgeBases.CreateKnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBases.CreateKnowledgeBase]. parent (:class:`str`): Required. The project to create a knowledge base for. Format: ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - knowledge_base (:class:`~.gcd_knowledge_base.KnowledgeBase`): + knowledge_base (:class:`google.cloud.dialogflow_v2beta1.types.KnowledgeBase`): Required. The knowledge base to create. + This corresponds to the ``knowledge_base`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -357,19 +390,19 @@ async def create_knowledge_base( sent along with the request as metadata. Returns: - ~.gcd_knowledge_base.KnowledgeBase: - A knowledge base represents a collection of knowledge - documents that you provide to Dialogflow. Your knowledge - documents contain information that may be useful during - conversations with end-users. Some Dialogflow features - use knowledge bases when looking for a response to an - end-user input. + google.cloud.dialogflow_v2beta1.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases`` resource is - deprecated; only use ``projects.knowledgeBases``. + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. """ # Create or coerce a protobuf request object. @@ -427,13 +460,14 @@ async def delete_knowledge_base( deprecated; only use ``projects.knowledgeBases``. Args: - request (:class:`~.knowledge_base.DeleteKnowledgeBaseRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.DeleteKnowledgeBaseRequest`): The request object. Request message for [KnowledgeBases.DeleteKnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBases.DeleteKnowledgeBase]. name (:class:`str`): Required. The name of the knowledge base to delete. Format: ``projects//locations//knowledgeBases/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -497,19 +531,21 @@ async def update_knowledge_base( deprecated; only use ``projects.knowledgeBases``. Args: - request (:class:`~.gcd_knowledge_base.UpdateKnowledgeBaseRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.UpdateKnowledgeBaseRequest`): The request object. Request message for [KnowledgeBases.UpdateKnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBases.UpdateKnowledgeBase]. - knowledge_base (:class:`~.gcd_knowledge_base.KnowledgeBase`): + knowledge_base (:class:`google.cloud.dialogflow_v2beta1.types.KnowledgeBase`): Required. The knowledge base to update. + This corresponds to the ``knowledge_base`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): Optional. Not specified means ``update all``. Currently, only ``display_name`` can be updated, an InvalidArgument will be returned for attempting to update other fields. + This corresponds to the ``update_mask`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -521,19 +557,19 @@ async def update_knowledge_base( sent along with the request as metadata. Returns: - ~.gcd_knowledge_base.KnowledgeBase: - A knowledge base represents a collection of knowledge - documents that you provide to Dialogflow. Your knowledge - documents contain information that may be useful during - conversations with end-users. Some Dialogflow features - use knowledge bases when looking for a response to an - end-user input. - - For more information, see the `knowledge base - guide `__. - - Note: The ``projects.agent.knowledgeBases`` resource is - deprecated; only use ``projects.knowledgeBases``. + google.cloud.dialogflow_v2beta1.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. """ # Create or coerce a protobuf request object. diff --git a/google/cloud/dialogflow_v2beta1/services/knowledge_bases/client.py b/google/cloud/dialogflow_v2beta1/services/knowledge_bases/client.py index 6df8d15e4..366e4f7ad 100644 --- a/google/cloud/dialogflow_v2beta1/services/knowledge_bases/client.py +++ b/google/cloud/dialogflow_v2beta1/services/knowledge_bases/client.py @@ -114,6 +114,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + KnowledgeBasesClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -126,7 +142,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + KnowledgeBasesClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -233,10 +249,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.KnowledgeBasesTransport]): The + transport (Union[str, KnowledgeBasesTransport]): The transport to use. If set to None, a transport is chosen automatically. - client_options (client_options_lib.ClientOptions): Custom options for the + client_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -272,21 +288,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -329,7 +341,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -349,13 +361,14 @@ def list_knowledge_bases( deprecated; only use ``projects.knowledgeBases``. Args: - request (:class:`~.knowledge_base.ListKnowledgeBasesRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListKnowledgeBasesRequest): The request object. Request message for [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2beta1.KnowledgeBases.ListKnowledgeBases]. - parent (:class:`str`): + parent (str): Required. The project to list of knowledge bases for. Format: ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -367,7 +380,7 @@ def list_knowledge_bases( sent along with the request as metadata. Returns: - ~.pagers.ListKnowledgeBasesPager: + google.cloud.dialogflow_v2beta1.services.knowledge_bases.pagers.ListKnowledgeBasesPager: Response message for [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2beta1.KnowledgeBases.ListKnowledgeBases]. @@ -435,13 +448,14 @@ def get_knowledge_base( deprecated; only use ``projects.knowledgeBases``. Args: - request (:class:`~.knowledge_base.GetKnowledgeBaseRequest`): + request (google.cloud.dialogflow_v2beta1.types.GetKnowledgeBaseRequest): The request object. Request message for [KnowledgeBases.GetKnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBases.GetKnowledgeBase]. - name (:class:`str`): + name (str): Required. The name of the knowledge base to retrieve. Format ``projects//locations//knowledgeBases/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -453,19 +467,19 @@ def get_knowledge_base( sent along with the request as metadata. Returns: - ~.knowledge_base.KnowledgeBase: - A knowledge base represents a collection of knowledge - documents that you provide to Dialogflow. Your knowledge - documents contain information that may be useful during - conversations with end-users. Some Dialogflow features - use knowledge bases when looking for a response to an - end-user input. + google.cloud.dialogflow_v2beta1.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases`` resource is - deprecated; only use ``projects.knowledgeBases``. + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. """ # Create or coerce a protobuf request object. @@ -523,19 +537,21 @@ def create_knowledge_base( deprecated; only use ``projects.knowledgeBases``. Args: - request (:class:`~.gcd_knowledge_base.CreateKnowledgeBaseRequest`): + request (google.cloud.dialogflow_v2beta1.types.CreateKnowledgeBaseRequest): The request object. Request message for [KnowledgeBases.CreateKnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBases.CreateKnowledgeBase]. - parent (:class:`str`): + parent (str): Required. The project to create a knowledge base for. Format: ``projects//locations/``. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - knowledge_base (:class:`~.gcd_knowledge_base.KnowledgeBase`): + knowledge_base (google.cloud.dialogflow_v2beta1.types.KnowledgeBase): Required. The knowledge base to create. + This corresponds to the ``knowledge_base`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -547,19 +563,19 @@ def create_knowledge_base( sent along with the request as metadata. Returns: - ~.gcd_knowledge_base.KnowledgeBase: - A knowledge base represents a collection of knowledge - documents that you provide to Dialogflow. Your knowledge - documents contain information that may be useful during - conversations with end-users. Some Dialogflow features - use knowledge bases when looking for a response to an - end-user input. + google.cloud.dialogflow_v2beta1.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. - For more information, see the `knowledge base - guide `__. + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). - Note: The ``projects.agent.knowledgeBases`` resource is - deprecated; only use ``projects.knowledgeBases``. + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. """ # Create or coerce a protobuf request object. @@ -618,13 +634,14 @@ def delete_knowledge_base( deprecated; only use ``projects.knowledgeBases``. Args: - request (:class:`~.knowledge_base.DeleteKnowledgeBaseRequest`): + request (google.cloud.dialogflow_v2beta1.types.DeleteKnowledgeBaseRequest): The request object. Request message for [KnowledgeBases.DeleteKnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBases.DeleteKnowledgeBase]. - name (:class:`str`): + name (str): Required. The name of the knowledge base to delete. Format: ``projects//locations//knowledgeBases/``. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -689,19 +706,21 @@ def update_knowledge_base( deprecated; only use ``projects.knowledgeBases``. Args: - request (:class:`~.gcd_knowledge_base.UpdateKnowledgeBaseRequest`): + request (google.cloud.dialogflow_v2beta1.types.UpdateKnowledgeBaseRequest): The request object. Request message for [KnowledgeBases.UpdateKnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBases.UpdateKnowledgeBase]. - knowledge_base (:class:`~.gcd_knowledge_base.KnowledgeBase`): + knowledge_base (google.cloud.dialogflow_v2beta1.types.KnowledgeBase): Required. The knowledge base to update. + This corresponds to the ``knowledge_base`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - update_mask (:class:`~.field_mask.FieldMask`): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. Not specified means ``update all``. Currently, only ``display_name`` can be updated, an InvalidArgument will be returned for attempting to update other fields. + This corresponds to the ``update_mask`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -713,19 +732,19 @@ def update_knowledge_base( sent along with the request as metadata. Returns: - ~.gcd_knowledge_base.KnowledgeBase: - A knowledge base represents a collection of knowledge - documents that you provide to Dialogflow. Your knowledge - documents contain information that may be useful during - conversations with end-users. Some Dialogflow features - use knowledge bases when looking for a response to an - end-user input. - - For more information, see the `knowledge base - guide `__. - - Note: The ``projects.agent.knowledgeBases`` resource is - deprecated; only use ``projects.knowledgeBases``. + google.cloud.dialogflow_v2beta1.types.KnowledgeBase: + A knowledge base represents a collection of knowledge documents that you + provide to Dialogflow. Your knowledge documents + contain information that may be useful during + conversations with end-users. Some Dialogflow + features use knowledge bases when looking for a + response to an end-user input. + + For more information, see the [knowledge base + guide](\ https://cloud.google.com/dialogflow/docs/how/knowledge-bases). + + Note: The projects.agent.knowledgeBases resource is + deprecated; only use projects.knowledgeBases. """ # Create or coerce a protobuf request object. diff --git a/google/cloud/dialogflow_v2beta1/services/knowledge_bases/pagers.py b/google/cloud/dialogflow_v2beta1/services/knowledge_bases/pagers.py index f58d0bcb7..32d735175 100644 --- a/google/cloud/dialogflow_v2beta1/services/knowledge_bases/pagers.py +++ b/google/cloud/dialogflow_v2beta1/services/knowledge_bases/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2beta1.types import knowledge_base @@ -24,7 +33,7 @@ class ListKnowledgeBasesPager: """A pager for iterating through ``list_knowledge_bases`` requests. This class thinly wraps an initial - :class:`~.knowledge_base.ListKnowledgeBasesResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListKnowledgeBasesResponse` object, and provides an ``__iter__`` method to iterate through its ``knowledge_bases`` field. @@ -33,7 +42,7 @@ class ListKnowledgeBasesPager: through the ``knowledge_bases`` field on the corresponding responses. - All the usual :class:`~.knowledge_base.ListKnowledgeBasesResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListKnowledgeBasesResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.knowledge_base.ListKnowledgeBasesRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListKnowledgeBasesRequest): The initial request object. - response (:class:`~.knowledge_base.ListKnowledgeBasesResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListKnowledgeBasesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ class ListKnowledgeBasesAsyncPager: """A pager for iterating through ``list_knowledge_bases`` requests. This class thinly wraps an initial - :class:`~.knowledge_base.ListKnowledgeBasesResponse` object, and + :class:`google.cloud.dialogflow_v2beta1.types.ListKnowledgeBasesResponse` object, and provides an ``__aiter__`` method to iterate through its ``knowledge_bases`` field. @@ -95,7 +104,7 @@ class ListKnowledgeBasesAsyncPager: through the ``knowledge_bases`` field on the corresponding responses. - All the usual :class:`~.knowledge_base.ListKnowledgeBasesResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListKnowledgeBasesResponse` attributes are available on the pager. If multiple requests are made, only the most recent response is retained, and thus used for attribute lookup. """ @@ -113,9 +122,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.knowledge_base.ListKnowledgeBasesRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListKnowledgeBasesRequest): The initial request object. - response (:class:`~.knowledge_base.ListKnowledgeBasesResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListKnowledgeBasesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/grpc.py index 1ab4180c5..88d8173f4 100644 --- a/google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/grpc.py +++ b/google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/grpc.py @@ -60,6 +60,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -90,6 +91,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -106,6 +111,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -115,11 +125,6 @@ def __init__( 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 @@ -163,12 +168,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/grpc_asyncio.py index 11ae20e12..19f40dc07 100644 --- a/google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/grpc_asyncio.py @@ -104,6 +104,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -135,6 +136,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -151,6 +156,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -160,11 +170,6 @@ def __init__( 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 @@ -208,12 +213,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/participants/__init__.py b/google/cloud/dialogflow_v2beta1/services/participants/__init__.py new file mode 100644 index 000000000..9ebfb1f70 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/participants/__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 ParticipantsClient +from .async_client import ParticipantsAsyncClient + +__all__ = ( + "ParticipantsClient", + "ParticipantsAsyncClient", +) diff --git a/google/cloud/dialogflow_v2beta1/services/participants/async_client.py b/google/cloud/dialogflow_v2beta1/services/participants/async_client.py new file mode 100644 index 000000000..a9ede9faa --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/participants/async_client.py @@ -0,0 +1,1124 @@ +# -*- 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.dialogflow_v2beta1.services.participants import pagers +from google.cloud.dialogflow_v2beta1.types import participant +from google.cloud.dialogflow_v2beta1.types import participant as gcd_participant +from google.cloud.dialogflow_v2beta1.types import session +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import ParticipantsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc_asyncio import ParticipantsGrpcAsyncIOTransport +from .client import ParticipantsClient + + +class ParticipantsAsyncClient: + """Service for managing + [Participants][google.cloud.dialogflow.v2beta1.Participant]. + """ + + _client: ParticipantsClient + + DEFAULT_ENDPOINT = ParticipantsClient.DEFAULT_ENDPOINT + DEFAULT_MTLS_ENDPOINT = ParticipantsClient.DEFAULT_MTLS_ENDPOINT + + context_path = staticmethod(ParticipantsClient.context_path) + parse_context_path = staticmethod(ParticipantsClient.parse_context_path) + document_path = staticmethod(ParticipantsClient.document_path) + parse_document_path = staticmethod(ParticipantsClient.parse_document_path) + intent_path = staticmethod(ParticipantsClient.intent_path) + parse_intent_path = staticmethod(ParticipantsClient.parse_intent_path) + message_path = staticmethod(ParticipantsClient.message_path) + parse_message_path = staticmethod(ParticipantsClient.parse_message_path) + participant_path = staticmethod(ParticipantsClient.participant_path) + parse_participant_path = staticmethod(ParticipantsClient.parse_participant_path) + session_entity_type_path = staticmethod(ParticipantsClient.session_entity_type_path) + parse_session_entity_type_path = staticmethod( + ParticipantsClient.parse_session_entity_type_path + ) + + common_billing_account_path = staticmethod( + ParticipantsClient.common_billing_account_path + ) + parse_common_billing_account_path = staticmethod( + ParticipantsClient.parse_common_billing_account_path + ) + + common_folder_path = staticmethod(ParticipantsClient.common_folder_path) + parse_common_folder_path = staticmethod(ParticipantsClient.parse_common_folder_path) + + common_organization_path = staticmethod(ParticipantsClient.common_organization_path) + parse_common_organization_path = staticmethod( + ParticipantsClient.parse_common_organization_path + ) + + common_project_path = staticmethod(ParticipantsClient.common_project_path) + parse_common_project_path = staticmethod( + ParticipantsClient.parse_common_project_path + ) + + common_location_path = staticmethod(ParticipantsClient.common_location_path) + parse_common_location_path = staticmethod( + ParticipantsClient.parse_common_location_path + ) + + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ParticipantsAsyncClient: The constructed client. + """ + return ParticipantsClient.from_service_account_info.__func__(ParticipantsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ParticipantsAsyncClient: The constructed client. + """ + return ParticipantsClient.from_service_account_file.__func__(ParticipantsAsyncClient, filename, *args, **kwargs) # type: ignore + + from_service_account_json = from_service_account_file + + @property + def transport(self) -> ParticipantsTransport: + """Return the transport used by the client instance. + + Returns: + ParticipantsTransport: The transport used by the client instance. + """ + return self._client.transport + + get_transport_class = functools.partial( + type(ParticipantsClient).get_transport_class, type(ParticipantsClient) + ) + + def __init__( + self, + *, + credentials: credentials.Credentials = None, + transport: Union[str, ParticipantsTransport] = "grpc_asyncio", + client_options: ClientOptions = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the participants 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, ~.ParticipantsTransport]): 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 = ParticipantsClient( + credentials=credentials, + transport=transport, + client_options=client_options, + client_info=client_info, + ) + + async def create_participant( + self, + request: gcd_participant.CreateParticipantRequest = None, + *, + parent: str = None, + participant: gcd_participant.Participant = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.Participant: + r"""Creates a new participant in a conversation. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.CreateParticipantRequest`): + The request object. The request message for + [Participants.CreateParticipant][google.cloud.dialogflow.v2beta1.Participants.CreateParticipant]. + parent (:class:`str`): + Required. Resource identifier of the conversation adding + the participant. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + participant (:class:`google.cloud.dialogflow_v2beta1.types.Participant`): + Required. The participant to create. + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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, participant]) + if request is 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 = gcd_participant.CreateParticipantRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if participant is not None: + request.participant = participant + + # 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_participant, + 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_participant( + self, + request: participant.GetParticipantRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.Participant: + r"""Retrieves a conversation participant. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.GetParticipantRequest`): + The request object. The request message for + [Participants.GetParticipant][google.cloud.dialogflow.v2beta1.Participants.GetParticipant]. + name (:class:`str`): + Required. The name of the participant. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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 = participant.GetParticipantRequest(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_participant, + 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 list_participants( + self, + request: participant.ListParticipantsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListParticipantsAsyncPager: + r"""Returns the list of all participants in the specified + conversation. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.ListParticipantsRequest`): + The request object. The request message for + [Participants.ListParticipants][google.cloud.dialogflow.v2beta1.Participants.ListParticipants]. + parent (:class:`str`): + Required. The conversation to list all participants + from. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.services.participants.pagers.ListParticipantsAsyncPager: + The response message for + [Participants.ListParticipants][google.cloud.dialogflow.v2beta1.Participants.ListParticipants]. + + 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 = participant.ListParticipantsRequest(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_participants, + 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.ListParticipantsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def update_participant( + self, + request: gcd_participant.UpdateParticipantRequest = None, + *, + participant: gcd_participant.Participant = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.Participant: + r"""Updates the specified participant. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.UpdateParticipantRequest`): + The request object. The request message for + [Participants.UpdateParticipant][google.cloud.dialogflow.v2beta1.Participants.UpdateParticipant]. + participant (:class:`google.cloud.dialogflow_v2beta1.types.Participant`): + Required. The participant to update. + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): + Required. The mask to specify which + fields to update. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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([participant, 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 = gcd_participant.UpdateParticipantRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if participant is not None: + request.participant = participant + 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_participant, + 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( + (("participant.name", request.participant.name),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def analyze_content( + self, + request: gcd_participant.AnalyzeContentRequest = None, + *, + participant: str = None, + text_input: session.TextInput = None, + audio_input: gcd_participant.AudioInput = None, + event_input: session.EventInput = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.AnalyzeContentResponse: + r"""Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.AnalyzeContentRequest`): + The request object. The request message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent]. + participant (:class:`str`): + Required. The name of the participant this text comes + from. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + text_input (:class:`google.cloud.dialogflow_v2beta1.types.TextInput`): + The natural language text to be + processed. + + This corresponds to the ``text_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + audio_input (:class:`google.cloud.dialogflow_v2beta1.types.AudioInput`): + The natural language speech audio to + be processed. + + This corresponds to the ``audio_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + event_input (:class:`google.cloud.dialogflow_v2beta1.types.EventInput`): + An input event to send to Dialogflow. + This corresponds to the ``event_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.AnalyzeContentResponse: + The response message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent]. + + """ + # Create or coerce a 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([participant, text_input, audio_input, event_input]) + if request is 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 = gcd_participant.AnalyzeContentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if participant is not None: + request.participant = participant + if text_input is not None: + request.text_input = text_input + if audio_input is not None: + request.audio_input = audio_input + if event_input is not None: + request.event_input = event_input + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.analyze_content, + 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( + (("participant", request.participant),) + ), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def streaming_analyze_content( + self, + requests: AsyncIterator[participant.StreamingAnalyzeContentRequest] = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> Awaitable[AsyncIterable[participant.StreamingAnalyzeContentResponse]]: + r"""Adds a text (e.g., chat) or audio (e.g., phone recording) + message from a participant into the conversation. Note: This + method is only available through the gRPC API (not REST). + + The top-level message sent to the client by the server is + ``StreamingAnalyzeContentResponse``. Multiple response messages + can be returned in order. The first one or more messages contain + the ``recognition_result`` field. Each result represents a more + complete transcript of what the user said. The next message + contains the ``reply_text`` field, and potentially the + ``reply_audio`` and/or the ``automated_agent_reply`` fields. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Args: + requests (AsyncIterator[`google.cloud.dialogflow_v2beta1.types.StreamingAnalyzeContentRequest`]): + The request object AsyncIterator. The top-level message sent by the + client to the + [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.StreamingAnalyzeContent] + method. + Multiple request messages should be sent in order: + + 1. The first message must contain + [participant][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.participant], + [config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] + and optionally + [query_params][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.query_params]. + If you want to receive an audio response, it should + also contain + [reply_audio_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.reply_audio_config]. + The message must not contain + [input][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input]. + 2. If + [config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] + in the first message was set to + [audio_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.audio_config], + all subsequent messages must contain + [input_audio][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_audio] + to continue with Speech recognition. + If you decide to rather analyze text input after you + already started Speech recognition, please send a + message with + [StreamingAnalyzeContentRequest.input_text][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_text]. + However, note that: + + * Dialogflow will bill you for the audio so far. + * Dialogflow discards all Speech recognition results + in favor of the text input. + + 3. If + [StreamingAnalyzeContentRequest.config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] + in the first message was set to + [StreamingAnalyzeContentRequest.text_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.text_config], + then the second message must contain only + [input_text][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_text]. + Moreover, you must not send more than two messages. + 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[google.cloud.dialogflow_v2beta1.types.StreamingAnalyzeContentResponse]: + The top-level message returned from the + StreamingAnalyzeContent 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 next message contains reply_text and + optionally reply_audio returned by an agent. This + message may also contain automated_agent_reply. + + """ + + # 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_analyze_content, + 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 suggest_articles( + self, + request: participant.SuggestArticlesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.SuggestArticlesResponse: + r"""Gets suggested articles for a participant based on specific + historical messages. + + Note that + [ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] + will only list the auto-generated suggestions, while + [CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion] + will try to compile suggestion based on the provided + conversation context in the real time. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.SuggestArticlesRequest`): + The request object. The request message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles]. + parent (:class:`str`): + Required. The name of the participant to fetch + suggestion for. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.SuggestArticlesResponse: + The response message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles]. + + """ + # Create or coerce a 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 = participant.SuggestArticlesRequest(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.suggest_articles, + 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 suggest_faq_answers( + self, + request: participant.SuggestFaqAnswersRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.SuggestFaqAnswersResponse: + r"""Gets suggested faq answers for a participant based on + specific historical messages. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.SuggestFaqAnswersRequest`): + The request object. The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers]. + parent (:class:`str`): + Required. The name of the participant to fetch + suggestion for. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.SuggestFaqAnswersResponse: + The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers]. + + """ + # Create or coerce a 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 = participant.SuggestFaqAnswersRequest(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.suggest_faq_answers, + 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 suggest_smart_replies( + self, + request: participant.SuggestSmartRepliesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.SuggestSmartRepliesResponse: + r"""Gets smart replies for a participant based on + specific historical messages. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.SuggestSmartRepliesRequest`): + The request object. The request message for + [Participants.SuggestSmartReplies][google.cloud.dialogflow.v2beta1.Participants.SuggestSmartReplies]. + parent (:class:`str`): + Required. The name of the participant to fetch + suggestion for. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.SuggestSmartRepliesResponse: + The response message for + [Participants.SuggestSmartReplies][google.cloud.dialogflow.v2beta1.Participants.SuggestSmartReplies]. + + """ + # Create or coerce a 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 = participant.SuggestSmartRepliesRequest(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.suggest_smart_replies, + 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 list_suggestions( + self, + request: participant.ListSuggestionsRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListSuggestionsAsyncPager: + r"""Deprecated: Use inline suggestion, event based suggestion or + Suggestion\* API instead. See + [HumanAgentAssistantConfig.name][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.name] + for more details. Removal Date: 2020-09-01. + + Retrieves suggestions for live agents. + + This method should be used by human agent client software to + fetch auto generated suggestions in real-time, while the + conversation with an end user is in progress. The functionality + is implemented in terms of the `list + pagination `__ + design pattern. The client app should use the + ``next_page_token`` field to fetch the next batch of + suggestions. ``suggestions`` are sorted by ``create_time`` in + descending order. To fetch latest suggestion, just set + ``page_size`` to 1. To fetch new suggestions without + duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.ListSuggestionsRequest`): + The request object. The request message for + [Participants.ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions]. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.services.participants.pagers.ListSuggestionsAsyncPager: + The response message for + [Participants.ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions]. + + Iterating over this object will yield results and + resolve additional pages automatically. + + """ + # Create or coerce a protobuf request object. + + request = participant.ListSuggestionsRequest(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.list_suggestions, + 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.ListSuggestionsAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + async def compile_suggestion( + self, + request: participant.CompileSuggestionRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.CompileSuggestionResponse: + r"""Deprecated. use + [SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles] + and + [SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers] + instead. + + Gets suggestions for a participant based on specific historical + messages. + + Note that + [ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] + will only list the auto-generated suggestions, while + [CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion] + will try to compile suggestion based on the provided + conversation context in the real time. + + Args: + request (:class:`google.cloud.dialogflow_v2beta1.types.CompileSuggestionRequest`): + The request object. The request message for + [Participants.CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion]. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.CompileSuggestionResponse: + The response message for + [Participants.CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion]. + + """ + # Create or coerce a protobuf request object. + + request = participant.CompileSuggestionRequest(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.compile_suggestion, + 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 + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ParticipantsAsyncClient",) diff --git a/google/cloud/dialogflow_v2beta1/services/participants/client.py b/google/cloud/dialogflow_v2beta1/services/participants/client.py new file mode 100644 index 000000000..583b5f7c3 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/participants/client.py @@ -0,0 +1,1368 @@ +# -*- 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.dialogflow_v2beta1.services.participants import pagers +from google.cloud.dialogflow_v2beta1.types import participant +from google.cloud.dialogflow_v2beta1.types import participant as gcd_participant +from google.cloud.dialogflow_v2beta1.types import session +from google.protobuf import field_mask_pb2 as field_mask # type: ignore + +from .transports.base import ParticipantsTransport, DEFAULT_CLIENT_INFO +from .transports.grpc import ParticipantsGrpcTransport +from .transports.grpc_asyncio import ParticipantsGrpcAsyncIOTransport + + +class ParticipantsClientMeta(type): + """Metaclass for the Participants 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[ParticipantsTransport]] + _transport_registry["grpc"] = ParticipantsGrpcTransport + _transport_registry["grpc_asyncio"] = ParticipantsGrpcAsyncIOTransport + + def get_transport_class(cls, label: str = None,) -> Type[ParticipantsTransport]: + """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 ParticipantsClient(metaclass=ParticipantsClientMeta): + """Service for managing + [Participants][google.cloud.dialogflow.v2beta1.Participant]. + """ + + @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_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ParticipantsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + ParticipantsClient: 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) -> ParticipantsTransport: + """Return the transport used by the client instance. + + Returns: + ParticipantsTransport: The transport used by the client instance. + """ + return self._transport + + @staticmethod + def context_path(project: str, session: str, context: str,) -> str: + """Return a fully-qualified context string.""" + return "projects/{project}/agent/sessions/{session}/contexts/{context}".format( + project=project, session=session, context=context, + ) + + @staticmethod + def parse_context_path(path: str) -> Dict[str, str]: + """Parse a context path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/agent/sessions/(?P.+?)/contexts/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def document_path(project: str, knowledge_base: str, document: str,) -> str: + """Return a fully-qualified document string.""" + return "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, knowledge_base=knowledge_base, document=document, + ) + + @staticmethod + def parse_document_path(path: str) -> Dict[str, str]: + """Parse a document path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/knowledgeBases/(?P.+?)/documents/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def intent_path(project: str, intent: str,) -> str: + """Return a fully-qualified intent string.""" + return "projects/{project}/agent/intents/{intent}".format( + project=project, 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.+?)/agent/intents/(?P.+?)$", path) + return m.groupdict() if m else {} + + @staticmethod + def message_path(project: str, conversation: str, message: str,) -> str: + """Return a fully-qualified message string.""" + return "projects/{project}/conversations/{conversation}/messages/{message}".format( + project=project, conversation=conversation, message=message, + ) + + @staticmethod + def parse_message_path(path: str) -> Dict[str, str]: + """Parse a message path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversations/(?P.+?)/messages/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def participant_path(project: str, conversation: str, participant: str,) -> str: + """Return a fully-qualified participant string.""" + return "projects/{project}/conversations/{conversation}/participants/{participant}".format( + project=project, conversation=conversation, participant=participant, + ) + + @staticmethod + def parse_participant_path(path: str) -> Dict[str, str]: + """Parse a participant path into its component segments.""" + m = re.match( + r"^projects/(?P.+?)/conversations/(?P.+?)/participants/(?P.+?)$", + path, + ) + return m.groupdict() if m else {} + + @staticmethod + def session_entity_type_path(project: str, session: str, entity_type: str,) -> str: + """Return a fully-qualified session_entity_type string.""" + return "projects/{project}/agent/sessions/{session}/entityTypes/{entity_type}".format( + project=project, 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.+?)/agent/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, ParticipantsTransport, None] = None, + client_options: Optional[client_options_lib.ClientOptions] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the participants 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, ParticipantsTransport]): The + transport to use. If set to None, a transport is chosen + automatically. + client_options (google.api_core.client_options.ClientOptions): Custom options for the + client. It won't take effect if a ``transport`` instance is provided. + (1) The ``api_endpoint`` property can be used to override the + default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT + environment variable can also be used to override the endpoint: + "always" (always use the default mTLS endpoint), "never" (always + use the default regular endpoint) and "auto" (auto switch to the + default mTLS endpoint if client certificate is present, this is + the default value). However, the ``api_endpoint`` property takes + precedence if provided. + (2) If GOOGLE_API_USE_CLIENT_CERTIFICATE environment variable + is "true", then the ``client_cert_source`` property can be used + to provide client certificate for mutual TLS transport. If + not provided, the default SSL client certificate will be used if + present. If GOOGLE_API_USE_CLIENT_CERTIFICATE is "false" or not + set, no client certificate will be used. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + """ + if isinstance(client_options, dict): + client_options = client_options_lib.from_dict(client_options) + if client_options is None: + client_options = client_options_lib.ClientOptions() + + # Create SSL credentials for mutual TLS if needed. + use_client_cert = bool( + util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) + ) + + client_cert_source_func = None + is_mtls = False + if use_client_cert: + if client_options.client_cert_source: + is_mtls = True + client_cert_source_func = client_options.client_cert_source + else: + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) + + # Figure out which api endpoint to use. + if client_options.api_endpoint is not None: + api_endpoint = client_options.api_endpoint + else: + use_mtls_env = os.getenv("GOOGLE_API_USE_MTLS_ENDPOINT", "auto") + if use_mtls_env == "never": + api_endpoint = self.DEFAULT_ENDPOINT + elif use_mtls_env == "always": + api_endpoint = self.DEFAULT_MTLS_ENDPOINT + elif use_mtls_env == "auto": + api_endpoint = ( + self.DEFAULT_MTLS_ENDPOINT if is_mtls else self.DEFAULT_ENDPOINT + ) + else: + raise MutualTLSChannelError( + "Unsupported GOOGLE_API_USE_MTLS_ENDPOINT value. Accepted values: never, auto, always" + ) + + # Save or instantiate the transport. + # Ordinarily, we provide the transport, but allowing a custom transport + # instance provides an extensibility point for unusual situations. + if isinstance(transport, ParticipantsTransport): + # transport is a ParticipantsTransport instance. + if credentials or client_options.credentials_file: + raise ValueError( + "When providing a transport instance, " + "provide its credentials directly." + ) + if client_options.scopes: + raise ValueError( + "When providing a transport instance, " + "provide its scopes directly." + ) + self._transport = transport + else: + Transport = type(self).get_transport_class(transport) + self._transport = Transport( + credentials=credentials, + credentials_file=client_options.credentials_file, + host=api_endpoint, + scopes=client_options.scopes, + client_cert_source_for_mtls=client_cert_source_func, + quota_project_id=client_options.quota_project_id, + client_info=client_info, + ) + + def create_participant( + self, + request: gcd_participant.CreateParticipantRequest = None, + *, + parent: str = None, + participant: gcd_participant.Participant = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.Participant: + r"""Creates a new participant in a conversation. + + Args: + request (google.cloud.dialogflow_v2beta1.types.CreateParticipantRequest): + The request object. The request message for + [Participants.CreateParticipant][google.cloud.dialogflow.v2beta1.Participants.CreateParticipant]. + parent (str): + Required. Resource identifier of the conversation adding + the participant. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + participant (google.cloud.dialogflow_v2beta1.types.Participant): + Required. The participant to create. + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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, participant]) + if request is 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 gcd_participant.CreateParticipantRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_participant.CreateParticipantRequest): + request = gcd_participant.CreateParticipantRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if parent is not None: + request.parent = parent + if participant is not None: + request.participant = participant + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.create_participant] + + # 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_participant( + self, + request: participant.GetParticipantRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.Participant: + r"""Retrieves a conversation participant. + + Args: + request (google.cloud.dialogflow_v2beta1.types.GetParticipantRequest): + The request object. The request message for + [Participants.GetParticipant][google.cloud.dialogflow.v2beta1.Participants.GetParticipant]. + name (str): + Required. The name of the participant. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``name`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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 participant.GetParticipantRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, participant.GetParticipantRequest): + request = participant.GetParticipantRequest(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_participant] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def list_participants( + self, + request: participant.ListParticipantsRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListParticipantsPager: + r"""Returns the list of all participants in the specified + conversation. + + Args: + request (google.cloud.dialogflow_v2beta1.types.ListParticipantsRequest): + The request object. The request message for + [Participants.ListParticipants][google.cloud.dialogflow.v2beta1.Participants.ListParticipants]. + parent (str): + Required. The conversation to list all participants + from. Format: + ``projects//locations//conversations/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.services.participants.pagers.ListParticipantsPager: + The response message for + [Participants.ListParticipants][google.cloud.dialogflow.v2beta1.Participants.ListParticipants]. + + 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 participant.ListParticipantsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, participant.ListParticipantsRequest): + request = participant.ListParticipantsRequest(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_participants] + + # 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.ListParticipantsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def update_participant( + self, + request: gcd_participant.UpdateParticipantRequest = None, + *, + participant: gcd_participant.Participant = None, + update_mask: field_mask.FieldMask = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.Participant: + r"""Updates the specified participant. + + Args: + request (google.cloud.dialogflow_v2beta1.types.UpdateParticipantRequest): + The request object. The request message for + [Participants.UpdateParticipant][google.cloud.dialogflow.v2beta1.Participants.UpdateParticipant]. + participant (google.cloud.dialogflow_v2beta1.types.Participant): + Required. The participant to update. + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. The mask to specify which + fields to update. + + This corresponds to the ``update_mask`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.Participant: + Represents a conversation participant + (human agent, virtual agent, end-user). + + """ + # Create or coerce a 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([participant, 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 gcd_participant.UpdateParticipantRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_participant.UpdateParticipantRequest): + request = gcd_participant.UpdateParticipantRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if participant is not None: + request.participant = participant + 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_participant] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("participant.name", request.participant.name),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def analyze_content( + self, + request: gcd_participant.AnalyzeContentRequest = None, + *, + participant: str = None, + text_input: session.TextInput = None, + audio_input: gcd_participant.AudioInput = None, + event_input: session.EventInput = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> gcd_participant.AnalyzeContentResponse: + r"""Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Args: + request (google.cloud.dialogflow_v2beta1.types.AnalyzeContentRequest): + The request object. The request message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent]. + participant (str): + Required. The name of the participant this text comes + from. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``participant`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + text_input (google.cloud.dialogflow_v2beta1.types.TextInput): + The natural language text to be + processed. + + This corresponds to the ``text_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + audio_input (google.cloud.dialogflow_v2beta1.types.AudioInput): + The natural language speech audio to + be processed. + + This corresponds to the ``audio_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + event_input (google.cloud.dialogflow_v2beta1.types.EventInput): + An input event to send to Dialogflow. + This corresponds to the ``event_input`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.AnalyzeContentResponse: + The response message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent]. + + """ + # Create or coerce a 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([participant, text_input, audio_input, event_input]) + if request is 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 gcd_participant.AnalyzeContentRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, gcd_participant.AnalyzeContentRequest): + request = gcd_participant.AnalyzeContentRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + + if participant is not None: + request.participant = participant + if text_input is not None: + request.text_input = text_input + if audio_input is not None: + request.audio_input = audio_input + if event_input is not None: + request.event_input = event_input + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.analyze_content] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata( + (("participant", request.participant),) + ), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def streaming_analyze_content( + self, + requests: Iterator[participant.StreamingAnalyzeContentRequest] = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> Iterable[participant.StreamingAnalyzeContentResponse]: + r"""Adds a text (e.g., chat) or audio (e.g., phone recording) + message from a participant into the conversation. Note: This + method is only available through the gRPC API (not REST). + + The top-level message sent to the client by the server is + ``StreamingAnalyzeContentResponse``. Multiple response messages + can be returned in order. The first one or more messages contain + the ``recognition_result`` field. Each result represents a more + complete transcript of what the user said. The next message + contains the ``reply_text`` field, and potentially the + ``reply_audio`` and/or the ``automated_agent_reply`` fields. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Args: + requests (Iterator[google.cloud.dialogflow_v2beta1.types.StreamingAnalyzeContentRequest]): + The request object iterator. The top-level message sent by the + client to the + [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.StreamingAnalyzeContent] + method. + Multiple request messages should be sent in order: + + 1. The first message must contain + [participant][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.participant], + [config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] + and optionally + [query_params][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.query_params]. + If you want to receive an audio response, it should + also contain + [reply_audio_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.reply_audio_config]. + The message must not contain + [input][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input]. + 2. If + [config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] + in the first message was set to + [audio_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.audio_config], + all subsequent messages must contain + [input_audio][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_audio] + to continue with Speech recognition. + If you decide to rather analyze text input after you + already started Speech recognition, please send a + message with + [StreamingAnalyzeContentRequest.input_text][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_text]. + However, note that: + + * Dialogflow will bill you for the audio so far. + * Dialogflow discards all Speech recognition results + in favor of the text input. + + 3. If + [StreamingAnalyzeContentRequest.config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] + in the first message was set to + [StreamingAnalyzeContentRequest.text_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.text_config], + then the second message must contain only + [input_text][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_text]. + Moreover, you must not send more than two messages. + 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[google.cloud.dialogflow_v2beta1.types.StreamingAnalyzeContentResponse]: + The top-level message returned from the + StreamingAnalyzeContent 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 next message contains reply_text and + optionally reply_audio returned by an agent. This + message may also contain automated_agent_reply. + + """ + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[ + self._transport.streaming_analyze_content + ] + + # Send the request. + response = rpc(requests, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def suggest_articles( + self, + request: participant.SuggestArticlesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.SuggestArticlesResponse: + r"""Gets suggested articles for a participant based on specific + historical messages. + + Note that + [ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] + will only list the auto-generated suggestions, while + [CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion] + will try to compile suggestion based on the provided + conversation context in the real time. + + Args: + request (google.cloud.dialogflow_v2beta1.types.SuggestArticlesRequest): + The request object. The request message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles]. + parent (str): + Required. The name of the participant to fetch + suggestion for. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.SuggestArticlesResponse: + The response message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles]. + + """ + # Create or coerce a 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 participant.SuggestArticlesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, participant.SuggestArticlesRequest): + request = participant.SuggestArticlesRequest(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.suggest_articles] + + # 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 suggest_faq_answers( + self, + request: participant.SuggestFaqAnswersRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.SuggestFaqAnswersResponse: + r"""Gets suggested faq answers for a participant based on + specific historical messages. + + Args: + request (google.cloud.dialogflow_v2beta1.types.SuggestFaqAnswersRequest): + The request object. The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers]. + parent (str): + Required. The name of the participant to fetch + suggestion for. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.SuggestFaqAnswersResponse: + The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers]. + + """ + # Create or coerce a 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 participant.SuggestFaqAnswersRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, participant.SuggestFaqAnswersRequest): + request = participant.SuggestFaqAnswersRequest(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.suggest_faq_answers] + + # 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 suggest_smart_replies( + self, + request: participant.SuggestSmartRepliesRequest = None, + *, + parent: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.SuggestSmartRepliesResponse: + r"""Gets smart replies for a participant based on + specific historical messages. + + Args: + request (google.cloud.dialogflow_v2beta1.types.SuggestSmartRepliesRequest): + The request object. The request message for + [Participants.SuggestSmartReplies][google.cloud.dialogflow.v2beta1.Participants.SuggestSmartReplies]. + parent (str): + Required. The name of the participant to fetch + suggestion for. Format: + ``projects//locations//conversations//participants/``. + + This corresponds to the ``parent`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.SuggestSmartRepliesResponse: + The response message for + [Participants.SuggestSmartReplies][google.cloud.dialogflow.v2beta1.Participants.SuggestSmartReplies]. + + """ + # Create or coerce a 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 participant.SuggestSmartRepliesRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, participant.SuggestSmartRepliesRequest): + request = participant.SuggestSmartRepliesRequest(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.suggest_smart_replies] + + # 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 list_suggestions( + self, + request: participant.ListSuggestionsRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListSuggestionsPager: + r"""Deprecated: Use inline suggestion, event based suggestion or + Suggestion\* API instead. See + [HumanAgentAssistantConfig.name][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.name] + for more details. Removal Date: 2020-09-01. + + Retrieves suggestions for live agents. + + This method should be used by human agent client software to + fetch auto generated suggestions in real-time, while the + conversation with an end user is in progress. The functionality + is implemented in terms of the `list + pagination `__ + design pattern. The client app should use the + ``next_page_token`` field to fetch the next batch of + suggestions. ``suggestions`` are sorted by ``create_time`` in + descending order. To fetch latest suggestion, just set + ``page_size`` to 1. To fetch new suggestions without + duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Args: + request (google.cloud.dialogflow_v2beta1.types.ListSuggestionsRequest): + The request object. The request message for + [Participants.ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions]. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.services.participants.pagers.ListSuggestionsPager: + The response message for + [Participants.ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions]. + + Iterating over this object will yield results and + resolve additional pages automatically. + + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a participant.ListSuggestionsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, participant.ListSuggestionsRequest): + request = participant.ListSuggestionsRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.list_suggestions] + + # 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.ListSuggestionsPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + + def compile_suggestion( + self, + request: participant.CompileSuggestionRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> participant.CompileSuggestionResponse: + r"""Deprecated. use + [SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles] + and + [SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers] + instead. + + Gets suggestions for a participant based on specific historical + messages. + + Note that + [ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] + will only list the auto-generated suggestions, while + [CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion] + will try to compile suggestion based on the provided + conversation context in the real time. + + Args: + request (google.cloud.dialogflow_v2beta1.types.CompileSuggestionRequest): + The request object. The request message for + [Participants.CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion]. + + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.dialogflow_v2beta1.types.CompileSuggestionResponse: + The response message for + [Participants.CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion]. + + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a participant.CompileSuggestionRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, participant.CompileSuggestionRequest): + request = participant.CompileSuggestionRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.compile_suggestion] + + # 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 + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +__all__ = ("ParticipantsClient",) diff --git a/google/cloud/dialogflow_v2beta1/services/participants/pagers.py b/google/cloud/dialogflow_v2beta1/services/participants/pagers.py new file mode 100644 index 000000000..e2b2278e8 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/participants/pagers.py @@ -0,0 +1,285 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) + +from google.cloud.dialogflow_v2beta1.types import participant + + +class ListParticipantsPager: + """A pager for iterating through ``list_participants`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListParticipantsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``participants`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListParticipants`` requests and continue to iterate + through the ``participants`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListParticipantsResponse` + 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[..., participant.ListParticipantsResponse], + request: participant.ListParticipantsRequest, + response: participant.ListParticipantsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListParticipantsRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListParticipantsResponse): + 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 = participant.ListParticipantsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[participant.ListParticipantsResponse]: + 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[participant.Participant]: + for page in self.pages: + yield from page.participants + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListParticipantsAsyncPager: + """A pager for iterating through ``list_participants`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListParticipantsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``participants`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListParticipants`` requests and continue to iterate + through the ``participants`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListParticipantsResponse` + 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[participant.ListParticipantsResponse]], + request: participant.ListParticipantsRequest, + response: participant.ListParticipantsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListParticipantsRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListParticipantsResponse): + 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 = participant.ListParticipantsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[participant.ListParticipantsResponse]: + 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[participant.Participant]: + async def async_generator(): + async for page in self.pages: + for response in page.participants: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListSuggestionsPager: + """A pager for iterating through ``list_suggestions`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListSuggestionsResponse` object, and + provides an ``__iter__`` method to iterate through its + ``suggestions`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListSuggestions`` requests and continue to iterate + through the ``suggestions`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListSuggestionsResponse` + 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[..., participant.ListSuggestionsResponse], + request: participant.ListSuggestionsRequest, + response: participant.ListSuggestionsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListSuggestionsRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListSuggestionsResponse): + 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 = participant.ListSuggestionsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[participant.ListSuggestionsResponse]: + 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[participant.Suggestion]: + for page in self.pages: + yield from page.suggestions + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListSuggestionsAsyncPager: + """A pager for iterating through ``list_suggestions`` requests. + + This class thinly wraps an initial + :class:`google.cloud.dialogflow_v2beta1.types.ListSuggestionsResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``suggestions`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListSuggestions`` requests and continue to iterate + through the ``suggestions`` field on the + corresponding responses. + + All the usual :class:`google.cloud.dialogflow_v2beta1.types.ListSuggestionsResponse` + 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[participant.ListSuggestionsResponse]], + request: participant.ListSuggestionsRequest, + response: participant.ListSuggestionsResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.dialogflow_v2beta1.types.ListSuggestionsRequest): + The initial request object. + response (google.cloud.dialogflow_v2beta1.types.ListSuggestionsResponse): + 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 = participant.ListSuggestionsRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[participant.ListSuggestionsResponse]: + 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[participant.Suggestion]: + async def async_generator(): + async for page in self.pages: + for response in page.suggestions: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/dialogflow_v2beta1/services/participants/transports/__init__.py b/google/cloud/dialogflow_v2beta1/services/participants/transports/__init__.py new file mode 100644 index 000000000..91a4cec1c --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/participants/transports/__init__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from collections import OrderedDict +from typing import Dict, Type + +from .base import ParticipantsTransport +from .grpc import ParticipantsGrpcTransport +from .grpc_asyncio import ParticipantsGrpcAsyncIOTransport + + +# Compile a registry of transports. +_transport_registry = OrderedDict() # type: Dict[str, Type[ParticipantsTransport]] +_transport_registry["grpc"] = ParticipantsGrpcTransport +_transport_registry["grpc_asyncio"] = ParticipantsGrpcAsyncIOTransport + +__all__ = ( + "ParticipantsTransport", + "ParticipantsGrpcTransport", + "ParticipantsGrpcAsyncIOTransport", +) diff --git a/google/cloud/dialogflow_v2beta1/services/participants/transports/base.py b/google/cloud/dialogflow_v2beta1/services/participants/transports/base.py new file mode 100644 index 000000000..eda539300 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/participants/transports/base.py @@ -0,0 +1,290 @@ +# -*- 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.dialogflow_v2beta1.types import participant +from google.cloud.dialogflow_v2beta1.types import participant as gcd_participant + + +try: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( + gapic_version=pkg_resources.get_distribution( + "google-cloud-dialogflow", + ).version, + ) +except pkg_resources.DistributionNotFound: + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo() + + +class ParticipantsTransport(abc.ABC): + """Abstract transport class for Participants.""" + + 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_participant: gapic_v1.method.wrap_method( + self.create_participant, default_timeout=None, client_info=client_info, + ), + self.get_participant: gapic_v1.method.wrap_method( + self.get_participant, default_timeout=None, client_info=client_info, + ), + self.list_participants: gapic_v1.method.wrap_method( + self.list_participants, default_timeout=None, client_info=client_info, + ), + self.update_participant: gapic_v1.method.wrap_method( + self.update_participant, default_timeout=None, client_info=client_info, + ), + self.analyze_content: gapic_v1.method.wrap_method( + self.analyze_content, + 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_analyze_content: gapic_v1.method.wrap_method( + self.streaming_analyze_content, + default_timeout=220.0, + client_info=client_info, + ), + self.suggest_articles: gapic_v1.method.wrap_method( + self.suggest_articles, default_timeout=None, client_info=client_info, + ), + self.suggest_faq_answers: gapic_v1.method.wrap_method( + self.suggest_faq_answers, default_timeout=None, client_info=client_info, + ), + self.suggest_smart_replies: gapic_v1.method.wrap_method( + self.suggest_smart_replies, + default_timeout=None, + client_info=client_info, + ), + self.list_suggestions: gapic_v1.method.wrap_method( + self.list_suggestions, default_timeout=None, client_info=client_info, + ), + self.compile_suggestion: gapic_v1.method.wrap_method( + self.compile_suggestion, default_timeout=None, client_info=client_info, + ), + } + + @property + def create_participant( + self, + ) -> typing.Callable[ + [gcd_participant.CreateParticipantRequest], + typing.Union[ + gcd_participant.Participant, typing.Awaitable[gcd_participant.Participant] + ], + ]: + raise NotImplementedError() + + @property + def get_participant( + self, + ) -> typing.Callable[ + [participant.GetParticipantRequest], + typing.Union[ + participant.Participant, typing.Awaitable[participant.Participant] + ], + ]: + raise NotImplementedError() + + @property + def list_participants( + self, + ) -> typing.Callable[ + [participant.ListParticipantsRequest], + typing.Union[ + participant.ListParticipantsResponse, + typing.Awaitable[participant.ListParticipantsResponse], + ], + ]: + raise NotImplementedError() + + @property + def update_participant( + self, + ) -> typing.Callable[ + [gcd_participant.UpdateParticipantRequest], + typing.Union[ + gcd_participant.Participant, typing.Awaitable[gcd_participant.Participant] + ], + ]: + raise NotImplementedError() + + @property + def analyze_content( + self, + ) -> typing.Callable[ + [gcd_participant.AnalyzeContentRequest], + typing.Union[ + gcd_participant.AnalyzeContentResponse, + typing.Awaitable[gcd_participant.AnalyzeContentResponse], + ], + ]: + raise NotImplementedError() + + @property + def streaming_analyze_content( + self, + ) -> typing.Callable[ + [participant.StreamingAnalyzeContentRequest], + typing.Union[ + participant.StreamingAnalyzeContentResponse, + typing.Awaitable[participant.StreamingAnalyzeContentResponse], + ], + ]: + raise NotImplementedError() + + @property + def suggest_articles( + self, + ) -> typing.Callable[ + [participant.SuggestArticlesRequest], + typing.Union[ + participant.SuggestArticlesResponse, + typing.Awaitable[participant.SuggestArticlesResponse], + ], + ]: + raise NotImplementedError() + + @property + def suggest_faq_answers( + self, + ) -> typing.Callable[ + [participant.SuggestFaqAnswersRequest], + typing.Union[ + participant.SuggestFaqAnswersResponse, + typing.Awaitable[participant.SuggestFaqAnswersResponse], + ], + ]: + raise NotImplementedError() + + @property + def suggest_smart_replies( + self, + ) -> typing.Callable[ + [participant.SuggestSmartRepliesRequest], + typing.Union[ + participant.SuggestSmartRepliesResponse, + typing.Awaitable[participant.SuggestSmartRepliesResponse], + ], + ]: + raise NotImplementedError() + + @property + def list_suggestions( + self, + ) -> typing.Callable[ + [participant.ListSuggestionsRequest], + typing.Union[ + participant.ListSuggestionsResponse, + typing.Awaitable[participant.ListSuggestionsResponse], + ], + ]: + raise NotImplementedError() + + @property + def compile_suggestion( + self, + ) -> typing.Callable[ + [participant.CompileSuggestionRequest], + typing.Union[ + participant.CompileSuggestionResponse, + typing.Awaitable[participant.CompileSuggestionResponse], + ], + ]: + raise NotImplementedError() + + +__all__ = ("ParticipantsTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/participants/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/participants/transports/grpc.py new file mode 100644 index 000000000..959346114 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/participants/transports/grpc.py @@ -0,0 +1,625 @@ +# -*- 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.dialogflow_v2beta1.types import participant +from google.cloud.dialogflow_v2beta1.types import participant as gcd_participant + +from .base import ParticipantsTransport, DEFAULT_CLIENT_INFO + + +class ParticipantsGrpcTransport(ParticipantsTransport): + """gRPC backend transport for Participants. + + Service for managing + [Participants][google.cloud.dialogflow.v2beta1.Participant]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id: Optional[str] = None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional(Sequence[str])): A list of scopes. This argument is + ignored if ``channel`` is provided. + channel (Optional[grpc.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTLSChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + self._stubs = {} # type: Dict[str, Callable] + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + @classmethod + def create_channel( + cls, + host: str = "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 (Optional[str]): The host for the channel to use. + credentials (Optional[~.Credentials]): The + authorization credentials to attach to requests. These + credentials identify this application to the service. If + none are specified, the client will attempt to ascertain + the credentials from the environment. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is mutually exclusive with credentials. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + kwargs (Optional[dict]): Keyword arguments, which are passed to the + channel creation. + Returns: + grpc.Channel: A gRPC channel object. + + Raises: + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + scopes = scopes or cls.AUTH_SCOPES + return grpc_helpers.create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes, + quota_project_id=quota_project_id, + **kwargs, + ) + + @property + def grpc_channel(self) -> grpc.Channel: + """Return the channel designed to connect to this service. + """ + return self._grpc_channel + + @property + def create_participant( + self, + ) -> Callable[ + [gcd_participant.CreateParticipantRequest], gcd_participant.Participant + ]: + r"""Return a callable for the create participant method over gRPC. + + Creates a new participant in a conversation. + + Returns: + Callable[[~.CreateParticipantRequest], + ~.Participant]: + 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_participant" not in self._stubs: + self._stubs["create_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/CreateParticipant", + request_serializer=gcd_participant.CreateParticipantRequest.serialize, + response_deserializer=gcd_participant.Participant.deserialize, + ) + return self._stubs["create_participant"] + + @property + def get_participant( + self, + ) -> Callable[[participant.GetParticipantRequest], participant.Participant]: + r"""Return a callable for the get participant method over gRPC. + + Retrieves a conversation participant. + + Returns: + Callable[[~.GetParticipantRequest], + ~.Participant]: + 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_participant" not in self._stubs: + self._stubs["get_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/GetParticipant", + request_serializer=participant.GetParticipantRequest.serialize, + response_deserializer=participant.Participant.deserialize, + ) + return self._stubs["get_participant"] + + @property + def list_participants( + self, + ) -> Callable[ + [participant.ListParticipantsRequest], participant.ListParticipantsResponse + ]: + r"""Return a callable for the list participants method over gRPC. + + Returns the list of all participants in the specified + conversation. + + Returns: + Callable[[~.ListParticipantsRequest], + ~.ListParticipantsResponse]: + 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_participants" not in self._stubs: + self._stubs["list_participants"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/ListParticipants", + request_serializer=participant.ListParticipantsRequest.serialize, + response_deserializer=participant.ListParticipantsResponse.deserialize, + ) + return self._stubs["list_participants"] + + @property + def update_participant( + self, + ) -> Callable[ + [gcd_participant.UpdateParticipantRequest], gcd_participant.Participant + ]: + r"""Return a callable for the update participant method over gRPC. + + Updates the specified participant. + + Returns: + Callable[[~.UpdateParticipantRequest], + ~.Participant]: + 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_participant" not in self._stubs: + self._stubs["update_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/UpdateParticipant", + request_serializer=gcd_participant.UpdateParticipantRequest.serialize, + response_deserializer=gcd_participant.Participant.deserialize, + ) + return self._stubs["update_participant"] + + @property + def analyze_content( + self, + ) -> Callable[ + [gcd_participant.AnalyzeContentRequest], gcd_participant.AnalyzeContentResponse + ]: + r"""Return a callable for the analyze content method over gRPC. + + Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Returns: + Callable[[~.AnalyzeContentRequest], + ~.AnalyzeContentResponse]: + 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 "analyze_content" not in self._stubs: + self._stubs["analyze_content"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/AnalyzeContent", + request_serializer=gcd_participant.AnalyzeContentRequest.serialize, + response_deserializer=gcd_participant.AnalyzeContentResponse.deserialize, + ) + return self._stubs["analyze_content"] + + @property + def streaming_analyze_content( + self, + ) -> Callable[ + [participant.StreamingAnalyzeContentRequest], + participant.StreamingAnalyzeContentResponse, + ]: + r"""Return a callable for the streaming analyze content method over gRPC. + + Adds a text (e.g., chat) or audio (e.g., phone recording) + message from a participant into the conversation. Note: This + method is only available through the gRPC API (not REST). + + The top-level message sent to the client by the server is + ``StreamingAnalyzeContentResponse``. Multiple response messages + can be returned in order. The first one or more messages contain + the ``recognition_result`` field. Each result represents a more + complete transcript of what the user said. The next message + contains the ``reply_text`` field, and potentially the + ``reply_audio`` and/or the ``automated_agent_reply`` fields. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Returns: + Callable[[~.StreamingAnalyzeContentRequest], + ~.StreamingAnalyzeContentResponse]: + 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_analyze_content" not in self._stubs: + self._stubs["streaming_analyze_content"] = self.grpc_channel.stream_stream( + "/google.cloud.dialogflow.v2beta1.Participants/StreamingAnalyzeContent", + request_serializer=participant.StreamingAnalyzeContentRequest.serialize, + response_deserializer=participant.StreamingAnalyzeContentResponse.deserialize, + ) + return self._stubs["streaming_analyze_content"] + + @property + def suggest_articles( + self, + ) -> Callable[ + [participant.SuggestArticlesRequest], participant.SuggestArticlesResponse + ]: + r"""Return a callable for the suggest articles method over gRPC. + + Gets suggested articles for a participant based on specific + historical messages. + + Note that + [ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] + will only list the auto-generated suggestions, while + [CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion] + will try to compile suggestion based on the provided + conversation context in the real time. + + Returns: + Callable[[~.SuggestArticlesRequest], + ~.SuggestArticlesResponse]: + 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 "suggest_articles" not in self._stubs: + self._stubs["suggest_articles"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/SuggestArticles", + request_serializer=participant.SuggestArticlesRequest.serialize, + response_deserializer=participant.SuggestArticlesResponse.deserialize, + ) + return self._stubs["suggest_articles"] + + @property + def suggest_faq_answers( + self, + ) -> Callable[ + [participant.SuggestFaqAnswersRequest], participant.SuggestFaqAnswersResponse + ]: + r"""Return a callable for the suggest faq answers method over gRPC. + + Gets suggested faq answers for a participant based on + specific historical messages. + + Returns: + Callable[[~.SuggestFaqAnswersRequest], + ~.SuggestFaqAnswersResponse]: + 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 "suggest_faq_answers" not in self._stubs: + self._stubs["suggest_faq_answers"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/SuggestFaqAnswers", + request_serializer=participant.SuggestFaqAnswersRequest.serialize, + response_deserializer=participant.SuggestFaqAnswersResponse.deserialize, + ) + return self._stubs["suggest_faq_answers"] + + @property + def suggest_smart_replies( + self, + ) -> Callable[ + [participant.SuggestSmartRepliesRequest], + participant.SuggestSmartRepliesResponse, + ]: + r"""Return a callable for the suggest smart replies method over gRPC. + + Gets smart replies for a participant based on + specific historical messages. + + Returns: + Callable[[~.SuggestSmartRepliesRequest], + ~.SuggestSmartRepliesResponse]: + 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 "suggest_smart_replies" not in self._stubs: + self._stubs["suggest_smart_replies"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/SuggestSmartReplies", + request_serializer=participant.SuggestSmartRepliesRequest.serialize, + response_deserializer=participant.SuggestSmartRepliesResponse.deserialize, + ) + return self._stubs["suggest_smart_replies"] + + @property + def list_suggestions( + self, + ) -> Callable[ + [participant.ListSuggestionsRequest], participant.ListSuggestionsResponse + ]: + r"""Return a callable for the list suggestions method over gRPC. + + Deprecated: Use inline suggestion, event based suggestion or + Suggestion\* API instead. See + [HumanAgentAssistantConfig.name][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.name] + for more details. Removal Date: 2020-09-01. + + Retrieves suggestions for live agents. + + This method should be used by human agent client software to + fetch auto generated suggestions in real-time, while the + conversation with an end user is in progress. The functionality + is implemented in terms of the `list + pagination `__ + design pattern. The client app should use the + ``next_page_token`` field to fetch the next batch of + suggestions. ``suggestions`` are sorted by ``create_time`` in + descending order. To fetch latest suggestion, just set + ``page_size`` to 1. To fetch new suggestions without + duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Returns: + Callable[[~.ListSuggestionsRequest], + ~.ListSuggestionsResponse]: + 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_suggestions" not in self._stubs: + self._stubs["list_suggestions"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/ListSuggestions", + request_serializer=participant.ListSuggestionsRequest.serialize, + response_deserializer=participant.ListSuggestionsResponse.deserialize, + ) + return self._stubs["list_suggestions"] + + @property + def compile_suggestion( + self, + ) -> Callable[ + [participant.CompileSuggestionRequest], participant.CompileSuggestionResponse + ]: + r"""Return a callable for the compile suggestion method over gRPC. + + Deprecated. use + [SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles] + and + [SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers] + instead. + + Gets suggestions for a participant based on specific historical + messages. + + Note that + [ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] + will only list the auto-generated suggestions, while + [CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion] + will try to compile suggestion based on the provided + conversation context in the real time. + + Returns: + Callable[[~.CompileSuggestionRequest], + ~.CompileSuggestionResponse]: + 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 "compile_suggestion" not in self._stubs: + self._stubs["compile_suggestion"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/CompileSuggestion", + request_serializer=participant.CompileSuggestionRequest.serialize, + response_deserializer=participant.CompileSuggestionResponse.deserialize, + ) + return self._stubs["compile_suggestion"] + + +__all__ = ("ParticipantsGrpcTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/participants/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/participants/transports/grpc_asyncio.py new file mode 100644 index 000000000..0385717d2 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/services/participants/transports/grpc_asyncio.py @@ -0,0 +1,639 @@ +# -*- 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.dialogflow_v2beta1.types import participant +from google.cloud.dialogflow_v2beta1.types import participant as gcd_participant + +from .base import ParticipantsTransport, DEFAULT_CLIENT_INFO +from .grpc import ParticipantsGrpcTransport + + +class ParticipantsGrpcAsyncIOTransport(ParticipantsTransport): + """gRPC AsyncIO backend transport for Participants. + + Service for managing + [Participants][google.cloud.dialogflow.v2beta1.Participant]. + + 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, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, + quota_project_id=None, + client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, + ) -> None: + """Instantiate the transport. + + Args: + host (Optional[str]): The hostname to connect to. + credentials (Optional[google.auth.credentials.Credentials]): The + authorization credentials to attach to requests. These + credentials identify the application to the service; if none + are specified, the client will attempt to ascertain the + credentials from the environment. + This argument is ignored if ``channel`` is provided. + credentials_file (Optional[str]): A file with credentials that can + be loaded with :func:`google.auth.load_credentials_from_file`. + This argument is ignored if ``channel`` is provided. + scopes (Optional[Sequence[str]]): A optional list of scopes needed for this + service. These are only used when credentials are not specified and + are passed to :func:`google.auth.default`. + channel (Optional[aio.Channel]): A ``Channel`` instance through + which to make calls. + api_mtls_endpoint (Optional[str]): Deprecated. The mutual TLS endpoint. + If provided, it overrides the ``host`` argument and tries to create + a mutual TLS channel with client SSL credentials from + ``client_cert_source`` or applicatin default SSL credentials. + client_cert_source (Optional[Callable[[], Tuple[bytes, bytes]]]): + Deprecated. A callback to provide client SSL certificate bytes and + private key bytes, both in PEM format. It is ignored if + ``api_mtls_endpoint`` is None. + ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials + for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. + quota_project_id (Optional[str]): An optional project to use for billing + and quota. + client_info (google.api_core.gapic_v1.client_info.ClientInfo): + The client info used to send a user-agent string along with + API requests. If ``None``, then default info will be used. + Generally, you only need to set this if you're developing + your own client library. + + Raises: + google.auth.exceptions.MutualTlsChannelError: If mutual TLS transport + creation failed for any reason. + google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials`` + and ``credentials_file`` are passed. + """ + self._ssl_channel_credentials = ssl_channel_credentials + + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + + if channel: + # Sanity check: Ensure that channel and credentials are not both + # provided. + credentials = False + + # If a channel was explicitly provided, set it. + self._grpc_channel = channel + self._ssl_channel_credentials = None + elif api_mtls_endpoint: + host = ( + api_mtls_endpoint + if ":" in api_mtls_endpoint + else api_mtls_endpoint + ":443" + ) + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + # Create SSL credentials with client_cert_source or application + # default SSL credentials. + if client_cert_source: + cert, key = client_cert_source() + ssl_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + else: + ssl_credentials = SslCredentials().ssl_credentials + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=ssl_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + self._ssl_channel_credentials = ssl_credentials + else: + host = host if ":" in host else host + ":443" + + if credentials is None: + credentials, _ = auth.default( + scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id + ) + + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + + # create a new channel. The provided one is ignored. + self._grpc_channel = type(self).create_channel( + host, + credentials=credentials, + credentials_file=credentials_file, + ssl_credentials=self._ssl_channel_credentials, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Run the base constructor. + super().__init__( + host=host, + credentials=credentials, + credentials_file=credentials_file, + scopes=scopes or self.AUTH_SCOPES, + quota_project_id=quota_project_id, + client_info=client_info, + ) + + self._stubs = {} + + @property + def grpc_channel(self) -> aio.Channel: + """Create the channel designed to connect to this service. + + This property caches on the instance; repeated calls return + the same channel. + """ + # Return the channel from cache. + return self._grpc_channel + + @property + def create_participant( + self, + ) -> Callable[ + [gcd_participant.CreateParticipantRequest], + Awaitable[gcd_participant.Participant], + ]: + r"""Return a callable for the create participant method over gRPC. + + Creates a new participant in a conversation. + + Returns: + Callable[[~.CreateParticipantRequest], + Awaitable[~.Participant]]: + 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_participant" not in self._stubs: + self._stubs["create_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/CreateParticipant", + request_serializer=gcd_participant.CreateParticipantRequest.serialize, + response_deserializer=gcd_participant.Participant.deserialize, + ) + return self._stubs["create_participant"] + + @property + def get_participant( + self, + ) -> Callable[ + [participant.GetParticipantRequest], Awaitable[participant.Participant] + ]: + r"""Return a callable for the get participant method over gRPC. + + Retrieves a conversation participant. + + Returns: + Callable[[~.GetParticipantRequest], + Awaitable[~.Participant]]: + 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_participant" not in self._stubs: + self._stubs["get_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/GetParticipant", + request_serializer=participant.GetParticipantRequest.serialize, + response_deserializer=participant.Participant.deserialize, + ) + return self._stubs["get_participant"] + + @property + def list_participants( + self, + ) -> Callable[ + [participant.ListParticipantsRequest], + Awaitable[participant.ListParticipantsResponse], + ]: + r"""Return a callable for the list participants method over gRPC. + + Returns the list of all participants in the specified + conversation. + + Returns: + Callable[[~.ListParticipantsRequest], + Awaitable[~.ListParticipantsResponse]]: + 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_participants" not in self._stubs: + self._stubs["list_participants"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/ListParticipants", + request_serializer=participant.ListParticipantsRequest.serialize, + response_deserializer=participant.ListParticipantsResponse.deserialize, + ) + return self._stubs["list_participants"] + + @property + def update_participant( + self, + ) -> Callable[ + [gcd_participant.UpdateParticipantRequest], + Awaitable[gcd_participant.Participant], + ]: + r"""Return a callable for the update participant method over gRPC. + + Updates the specified participant. + + Returns: + Callable[[~.UpdateParticipantRequest], + Awaitable[~.Participant]]: + 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_participant" not in self._stubs: + self._stubs["update_participant"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/UpdateParticipant", + request_serializer=gcd_participant.UpdateParticipantRequest.serialize, + response_deserializer=gcd_participant.Participant.deserialize, + ) + return self._stubs["update_participant"] + + @property + def analyze_content( + self, + ) -> Callable[ + [gcd_participant.AnalyzeContentRequest], + Awaitable[gcd_participant.AnalyzeContentResponse], + ]: + r"""Return a callable for the analyze content method over gRPC. + + Adds a text (chat, for example), or audio (phone recording, for + example) message from a participant into the conversation. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Returns: + Callable[[~.AnalyzeContentRequest], + Awaitable[~.AnalyzeContentResponse]]: + 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 "analyze_content" not in self._stubs: + self._stubs["analyze_content"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/AnalyzeContent", + request_serializer=gcd_participant.AnalyzeContentRequest.serialize, + response_deserializer=gcd_participant.AnalyzeContentResponse.deserialize, + ) + return self._stubs["analyze_content"] + + @property + def streaming_analyze_content( + self, + ) -> Callable[ + [participant.StreamingAnalyzeContentRequest], + Awaitable[participant.StreamingAnalyzeContentResponse], + ]: + r"""Return a callable for the streaming analyze content method over gRPC. + + Adds a text (e.g., chat) or audio (e.g., phone recording) + message from a participant into the conversation. Note: This + method is only available through the gRPC API (not REST). + + The top-level message sent to the client by the server is + ``StreamingAnalyzeContentResponse``. Multiple response messages + can be returned in order. The first one or more messages contain + the ``recognition_result`` field. Each result represents a more + complete transcript of what the user said. The next message + contains the ``reply_text`` field, and potentially the + ``reply_audio`` and/or the ``automated_agent_reply`` fields. + + Note: Always use agent versions for production traffic sent to + virtual agents. See [Versions and + environments(https://cloud.google.com/dialogflow/es/docs/agents-versions). + + Returns: + Callable[[~.StreamingAnalyzeContentRequest], + Awaitable[~.StreamingAnalyzeContentResponse]]: + 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_analyze_content" not in self._stubs: + self._stubs["streaming_analyze_content"] = self.grpc_channel.stream_stream( + "/google.cloud.dialogflow.v2beta1.Participants/StreamingAnalyzeContent", + request_serializer=participant.StreamingAnalyzeContentRequest.serialize, + response_deserializer=participant.StreamingAnalyzeContentResponse.deserialize, + ) + return self._stubs["streaming_analyze_content"] + + @property + def suggest_articles( + self, + ) -> Callable[ + [participant.SuggestArticlesRequest], + Awaitable[participant.SuggestArticlesResponse], + ]: + r"""Return a callable for the suggest articles method over gRPC. + + Gets suggested articles for a participant based on specific + historical messages. + + Note that + [ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] + will only list the auto-generated suggestions, while + [CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion] + will try to compile suggestion based on the provided + conversation context in the real time. + + Returns: + Callable[[~.SuggestArticlesRequest], + Awaitable[~.SuggestArticlesResponse]]: + 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 "suggest_articles" not in self._stubs: + self._stubs["suggest_articles"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/SuggestArticles", + request_serializer=participant.SuggestArticlesRequest.serialize, + response_deserializer=participant.SuggestArticlesResponse.deserialize, + ) + return self._stubs["suggest_articles"] + + @property + def suggest_faq_answers( + self, + ) -> Callable[ + [participant.SuggestFaqAnswersRequest], + Awaitable[participant.SuggestFaqAnswersResponse], + ]: + r"""Return a callable for the suggest faq answers method over gRPC. + + Gets suggested faq answers for a participant based on + specific historical messages. + + Returns: + Callable[[~.SuggestFaqAnswersRequest], + Awaitable[~.SuggestFaqAnswersResponse]]: + 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 "suggest_faq_answers" not in self._stubs: + self._stubs["suggest_faq_answers"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/SuggestFaqAnswers", + request_serializer=participant.SuggestFaqAnswersRequest.serialize, + response_deserializer=participant.SuggestFaqAnswersResponse.deserialize, + ) + return self._stubs["suggest_faq_answers"] + + @property + def suggest_smart_replies( + self, + ) -> Callable[ + [participant.SuggestSmartRepliesRequest], + Awaitable[participant.SuggestSmartRepliesResponse], + ]: + r"""Return a callable for the suggest smart replies method over gRPC. + + Gets smart replies for a participant based on + specific historical messages. + + Returns: + Callable[[~.SuggestSmartRepliesRequest], + Awaitable[~.SuggestSmartRepliesResponse]]: + 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 "suggest_smart_replies" not in self._stubs: + self._stubs["suggest_smart_replies"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/SuggestSmartReplies", + request_serializer=participant.SuggestSmartRepliesRequest.serialize, + response_deserializer=participant.SuggestSmartRepliesResponse.deserialize, + ) + return self._stubs["suggest_smart_replies"] + + @property + def list_suggestions( + self, + ) -> Callable[ + [participant.ListSuggestionsRequest], + Awaitable[participant.ListSuggestionsResponse], + ]: + r"""Return a callable for the list suggestions method over gRPC. + + Deprecated: Use inline suggestion, event based suggestion or + Suggestion\* API instead. See + [HumanAgentAssistantConfig.name][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.name] + for more details. Removal Date: 2020-09-01. + + Retrieves suggestions for live agents. + + This method should be used by human agent client software to + fetch auto generated suggestions in real-time, while the + conversation with an end user is in progress. The functionality + is implemented in terms of the `list + pagination `__ + design pattern. The client app should use the + ``next_page_token`` field to fetch the next batch of + suggestions. ``suggestions`` are sorted by ``create_time`` in + descending order. To fetch latest suggestion, just set + ``page_size`` to 1. To fetch new suggestions without + duplication, send request with filter + ``create_time_epoch_microseconds > [first item's create_time of previous request]`` + and empty page_token. + + Returns: + Callable[[~.ListSuggestionsRequest], + Awaitable[~.ListSuggestionsResponse]]: + 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_suggestions" not in self._stubs: + self._stubs["list_suggestions"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/ListSuggestions", + request_serializer=participant.ListSuggestionsRequest.serialize, + response_deserializer=participant.ListSuggestionsResponse.deserialize, + ) + return self._stubs["list_suggestions"] + + @property + def compile_suggestion( + self, + ) -> Callable[ + [participant.CompileSuggestionRequest], + Awaitable[participant.CompileSuggestionResponse], + ]: + r"""Return a callable for the compile suggestion method over gRPC. + + Deprecated. use + [SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles] + and + [SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers] + instead. + + Gets suggestions for a participant based on specific historical + messages. + + Note that + [ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] + will only list the auto-generated suggestions, while + [CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion] + will try to compile suggestion based on the provided + conversation context in the real time. + + Returns: + Callable[[~.CompileSuggestionRequest], + Awaitable[~.CompileSuggestionResponse]]: + 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 "compile_suggestion" not in self._stubs: + self._stubs["compile_suggestion"] = self.grpc_channel.unary_unary( + "/google.cloud.dialogflow.v2beta1.Participants/CompileSuggestion", + request_serializer=participant.CompileSuggestionRequest.serialize, + response_deserializer=participant.CompileSuggestionResponse.deserialize, + ) + return self._stubs["compile_suggestion"] + + +__all__ = ("ParticipantsGrpcAsyncIOTransport",) diff --git a/google/cloud/dialogflow_v2beta1/services/session_entity_types/async_client.py b/google/cloud/dialogflow_v2beta1/services/session_entity_types/async_client.py index b5443f69d..1fac133d4 100644 --- a/google/cloud/dialogflow_v2beta1/services/session_entity_types/async_client.py +++ b/google/cloud/dialogflow_v2beta1/services/session_entity_types/async_client.py @@ -87,7 +87,36 @@ class SessionEntityTypesAsyncClient: SessionEntityTypesClient.parse_common_location_path ) - from_service_account_file = SessionEntityTypesClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionEntityTypesAsyncClient: The constructed client. + """ + return SessionEntityTypesClient.from_service_account_info.__func__(SessionEntityTypesAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionEntityTypesAsyncClient: The constructed client. + """ + return SessionEntityTypesClient.from_service_account_file.__func__(SessionEntityTypesAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -168,7 +197,7 @@ async def list_session_entity_types( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.ListSessionEntityTypesRequest`): The request object. The request message for [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.v2beta1.SessionEntityTypes.ListSessionEntityTypes]. parent (:class:`str`): @@ -184,6 +213,7 @@ async def list_session_entity_types( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -195,7 +225,7 @@ async def list_session_entity_types( sent along with the request as metadata. Returns: - ~.pagers.ListSessionEntityTypesAsyncPager: + google.cloud.dialogflow_v2beta1.services.session_entity_types.pagers.ListSessionEntityTypesAsyncPager: The response message for [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.v2beta1.SessionEntityTypes.ListSessionEntityTypes]. @@ -262,7 +292,7 @@ async def get_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.GetSessionEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.GetSessionEntityTypeRequest`): The request object. The request message for [SessionEntityTypes.GetSessionEntityType][google.cloud.dialogflow.v2beta1.SessionEntityTypes.GetSessionEntityType]. name (:class:`str`): @@ -278,6 +308,7 @@ async def get_session_entity_type( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -289,17 +320,17 @@ async def get_session_entity_type( sent along with the request as metadata. Returns: - ~.session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2beta1.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -359,7 +390,7 @@ async def create_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.gcd_session_entity_type.CreateSessionEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.CreateSessionEntityTypeRequest`): The request object. The request message for [SessionEntityTypes.CreateSessionEntityType][google.cloud.dialogflow.v2beta1.SessionEntityTypes.CreateSessionEntityType]. parent (:class:`str`): @@ -375,12 +406,14 @@ async def create_session_entity_type( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - session_entity_type (:class:`~.gcd_session_entity_type.SessionEntityType`): + session_entity_type (:class:`google.cloud.dialogflow_v2beta1.types.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. @@ -392,17 +425,17 @@ async def create_session_entity_type( sent along with the request as metadata. Returns: - ~.gcd_session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2beta1.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -461,18 +494,20 @@ async def update_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.gcd_session_entity_type.UpdateSessionEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.UpdateSessionEntityTypeRequest`): The request object. The request message for [SessionEntityTypes.UpdateSessionEntityType][google.cloud.dialogflow.v2beta1.SessionEntityTypes.UpdateSessionEntityType]. - session_entity_type (:class:`~.gcd_session_entity_type.SessionEntityType`): + session_entity_type (:class:`google.cloud.dialogflow_v2beta1.types.SessionEntityType`): Required. The session entity type to update. + 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`): + update_mask (:class:`google.protobuf.field_mask_pb2.FieldMask`): Optional. 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. @@ -484,17 +519,17 @@ async def update_session_entity_type( sent along with the request as metadata. Returns: - ~.gcd_session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2beta1.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -554,7 +589,7 @@ async def delete_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.DeleteSessionEntityTypeRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.DeleteSessionEntityTypeRequest`): The request object. The request message for [SessionEntityTypes.DeleteSessionEntityType][google.cloud.dialogflow.v2beta1.SessionEntityTypes.DeleteSessionEntityType]. name (:class:`str`): @@ -570,6 +605,7 @@ async def delete_session_entity_type( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. diff --git a/google/cloud/dialogflow_v2beta1/services/session_entity_types/client.py b/google/cloud/dialogflow_v2beta1/services/session_entity_types/client.py index 2a885ecc9..37ff5e1b2 100644 --- a/google/cloud/dialogflow_v2beta1/services/session_entity_types/client.py +++ b/google/cloud/dialogflow_v2beta1/services/session_entity_types/client.py @@ -119,6 +119,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionEntityTypesClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -131,7 +147,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + SessionEntityTypesClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -239,10 +255,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.SessionEntityTypesTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -278,21 +294,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -335,7 +347,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -356,10 +368,10 @@ def list_session_entity_types( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListSessionEntityTypesRequest): The request object. The request message for [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.v2beta1.SessionEntityTypes.ListSessionEntityTypes]. - parent (:class:`str`): + parent (str): Required. The session to list all session entity types from. Supported formats: @@ -372,6 +384,7 @@ def list_session_entity_types( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -383,7 +396,7 @@ def list_session_entity_types( sent along with the request as metadata. Returns: - ~.pagers.ListSessionEntityTypesPager: + google.cloud.dialogflow_v2beta1.services.session_entity_types.pagers.ListSessionEntityTypesPager: The response message for [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.v2beta1.SessionEntityTypes.ListSessionEntityTypes]. @@ -453,10 +466,10 @@ def get_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.GetSessionEntityTypeRequest`): + request (google.cloud.dialogflow_v2beta1.types.GetSessionEntityTypeRequest): The request object. The request message for [SessionEntityTypes.GetSessionEntityType][google.cloud.dialogflow.v2beta1.SessionEntityTypes.GetSessionEntityType]. - name (:class:`str`): + name (str): Required. The name of the session entity type. Supported formats: @@ -469,6 +482,7 @@ def get_session_entity_type( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -480,17 +494,17 @@ def get_session_entity_type( sent along with the request as metadata. Returns: - ~.session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2beta1.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -551,10 +565,10 @@ def create_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.gcd_session_entity_type.CreateSessionEntityTypeRequest`): + request (google.cloud.dialogflow_v2beta1.types.CreateSessionEntityTypeRequest): The request object. The request message for [SessionEntityTypes.CreateSessionEntityType][google.cloud.dialogflow.v2beta1.SessionEntityTypes.CreateSessionEntityType]. - parent (:class:`str`): + parent (str): Required. The session to create a session entity type for. Supported formats: @@ -567,12 +581,14 @@ def create_session_entity_type( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``parent`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - session_entity_type (:class:`~.gcd_session_entity_type.SessionEntityType`): + session_entity_type (google.cloud.dialogflow_v2beta1.types.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. @@ -584,17 +600,17 @@ def create_session_entity_type( sent along with the request as metadata. Returns: - ~.gcd_session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2beta1.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -658,18 +674,20 @@ def update_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.gcd_session_entity_type.UpdateSessionEntityTypeRequest`): + request (google.cloud.dialogflow_v2beta1.types.UpdateSessionEntityTypeRequest): The request object. The request message for [SessionEntityTypes.UpdateSessionEntityType][google.cloud.dialogflow.v2beta1.SessionEntityTypes.UpdateSessionEntityType]. - session_entity_type (:class:`~.gcd_session_entity_type.SessionEntityType`): + session_entity_type (google.cloud.dialogflow_v2beta1.types.SessionEntityType): Required. The session entity type to update. + 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`): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. 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. @@ -681,17 +699,17 @@ def update_session_entity_type( sent along with the request as metadata. Returns: - ~.gcd_session_entity_type.SessionEntityType: - A session represents a conversation between a Dialogflow - agent and an end-user. You can create special entities, - called session entities, during a session. Session - entities can extend or replace custom entity types and - only exist during the session that they were created - for. All session data, including session entities, is - stored by Dialogflow for 20 minutes. - - For more information, see the `session entity - guide `__. + google.cloud.dialogflow_v2beta1.types.SessionEntityType: + A session represents a conversation between a Dialogflow agent and an + end-user. You can create special entities, called + session entities, during a session. Session entities + can extend or replace custom entity types and only + exist during the session that they were created for. + All session data, including session entities, is + stored by Dialogflow for 20 minutes. + + For more information, see the [session entity + guide](\ https://cloud.google.com/dialogflow/docs/entities-session). """ # Create or coerce a protobuf request object. @@ -756,10 +774,10 @@ def delete_session_entity_type( use session entities with Google Assistant integration. Args: - request (:class:`~.session_entity_type.DeleteSessionEntityTypeRequest`): + request (google.cloud.dialogflow_v2beta1.types.DeleteSessionEntityTypeRequest): The request object. The request message for [SessionEntityTypes.DeleteSessionEntityType][google.cloud.dialogflow.v2beta1.SessionEntityTypes.DeleteSessionEntityType]. - name (:class:`str`): + name (str): Required. The name of the entity type to delete. Supported formats: @@ -772,6 +790,7 @@ def delete_session_entity_type( 'us' location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. + This corresponds to the ``name`` field on the ``request`` instance; if ``request`` is provided, this should not be set. diff --git a/google/cloud/dialogflow_v2beta1/services/session_entity_types/pagers.py b/google/cloud/dialogflow_v2beta1/services/session_entity_types/pagers.py index e883ae9e1..f92cbf966 100644 --- a/google/cloud/dialogflow_v2beta1/services/session_entity_types/pagers.py +++ b/google/cloud/dialogflow_v2beta1/services/session_entity_types/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.dialogflow_v2beta1.types import session_entity_type @@ -24,7 +33,7 @@ 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 + :class:`google.cloud.dialogflow_v2beta1.types.ListSessionEntityTypesResponse` object, and provides an ``__iter__`` method to iterate through its ``session_entity_types`` field. @@ -33,7 +42,7 @@ class ListSessionEntityTypesPager: through the ``session_entity_types`` field on the corresponding responses. - All the usual :class:`~.session_entity_type.ListSessionEntityTypesResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.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. """ @@ -51,9 +60,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListSessionEntityTypesRequest): The initial request object. - response (:class:`~.session_entity_type.ListSessionEntityTypesResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListSessionEntityTypesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. @@ -86,7 +95,7 @@ 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 + :class:`google.cloud.dialogflow_v2beta1.types.ListSessionEntityTypesResponse` object, and provides an ``__aiter__`` method to iterate through its ``session_entity_types`` field. @@ -95,7 +104,7 @@ class ListSessionEntityTypesAsyncPager: through the ``session_entity_types`` field on the corresponding responses. - All the usual :class:`~.session_entity_type.ListSessionEntityTypesResponse` + All the usual :class:`google.cloud.dialogflow_v2beta1.types.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. """ @@ -115,9 +124,9 @@ def __init__( Args: method (Callable): The method that was originally called, and which instantiated this pager. - request (:class:`~.session_entity_type.ListSessionEntityTypesRequest`): + request (google.cloud.dialogflow_v2beta1.types.ListSessionEntityTypesRequest): The initial request object. - response (:class:`~.session_entity_type.ListSessionEntityTypesResponse`): + response (google.cloud.dialogflow_v2beta1.types.ListSessionEntityTypesResponse): The initial response object. metadata (Sequence[Tuple[str, str]]): Strings which should be sent along with the request as metadata. diff --git a/google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/grpc.py index e87c07ebf..6fc05f9a9 100644 --- a/google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/grpc.py +++ b/google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/grpc.py @@ -62,6 +62,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -92,6 +93,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -108,6 +113,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -117,11 +127,6 @@ def __init__( 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 @@ -165,12 +170,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/grpc_asyncio.py index f5567c983..840eeb67d 100644 --- a/google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/grpc_asyncio.py @@ -106,6 +106,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -137,6 +138,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -153,6 +158,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -162,11 +172,6 @@ def __init__( 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 @@ -210,12 +215,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/sessions/async_client.py b/google/cloud/dialogflow_v2beta1/services/sessions/async_client.py index 2d9cd4bf1..ac247692e 100644 --- a/google/cloud/dialogflow_v2beta1/services/sessions/async_client.py +++ b/google/cloud/dialogflow_v2beta1/services/sessions/async_client.py @@ -93,7 +93,36 @@ class SessionsAsyncClient: 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 + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionsAsyncClient: The constructed client. + """ + return SessionsClient.from_service_account_info.__func__(SessionsAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionsAsyncClient: The constructed client. + """ + return SessionsClient.from_service_account_file.__func__(SessionsAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property @@ -177,7 +206,7 @@ async def detect_intent( environments `__. Args: - request (:class:`~.gcd_session.DetectIntentRequest`): + request (:class:`google.cloud.dialogflow_v2beta1.types.DetectIntentRequest`): The request object. The request to detect user's intent. session (:class:`str`): Required. The name of the session this query is sent to. @@ -190,23 +219,26 @@ async def detect_intent( If ``Location ID`` is not specified we assume default 'us' location. If ``Environment ID`` is not specified, - we assume default 'draft' environment. If ``User ID`` is - not specified, we are using "-". It's up to the API - caller to choose an appropriate ``Session ID`` and - ``User Id``. They can be a random number or some type of - user and session identifiers (preferably hashed). The - length of the ``Session ID`` and ``User ID`` must not - exceed 36 characters. For more information, see the `API + we assume default 'draft' environment + (``Environment ID`` might be referred to as environment + name at some places). If ``User ID`` is not specified, + we are using "-". It's up to the API caller to choose an + appropriate ``Session ID`` and ``User Id``. They can be + a random number or some type of user and session + identifiers (preferably hashed). The length of the + ``Session ID`` and ``User ID`` must not exceed 36 + characters. For more information, see the `API interactions guide `__. Note: Always use agent versions for production traffic. See `Versions and environments `__. + This corresponds to the ``session`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - query_input (:class:`~.gcd_session.QueryInput`): + query_input (:class:`google.cloud.dialogflow_v2beta1.types.QueryInput`): Required. The input specification. It can be set to: 1. an audio config @@ -217,6 +249,7 @@ async def detect_intent( of text, or 3. an event that specifies which intent to trigger. + This corresponds to the ``query_input`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -228,7 +261,7 @@ async def detect_intent( sent along with the request as metadata. Returns: - ~.gcd_session.DetectIntentResponse: + google.cloud.dialogflow_v2beta1.types.DetectIntentResponse: The message returned from the DetectIntent method. @@ -297,7 +330,7 @@ def streaming_detect_intent( environments `__. Args: - requests (AsyncIterator[`~.session.StreamingDetectIntentRequest`]): + requests (AsyncIterator[`google.cloud.dialogflow_v2beta1.types.StreamingDetectIntentRequest`]): The request object AsyncIterator. The top-level message sent by the client to the [Sessions.StreamingDetectIntent][google.cloud.dialogflow.v2beta1.Sessions.StreamingDetectIntent] @@ -341,28 +374,24 @@ def streaming_detect_intent( sent along with the request as metadata. Returns: - AsyncIterable[~.session.StreamingDetectIntentResponse]: + AsyncIterable[google.cloud.dialogflow_v2beta1.types.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 next message contains ``response_id``, - ``query_result``, ``alternative_query_results`` and - optionally ``webhook_status`` if a WebHook was - called. - - 3. If ``output_audio_config`` was specified in the - request or agent-level speech synthesizer is - configured, all subsequent messages contain - ``output_audio`` and ``output_audio_config``. + 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 next message contains response_id, + query_result, alternative_query_results and + optionally webhook_status if a WebHook was called. + 3. If output_audio_config was specified in the + request or agent-level speech synthesizer is + configured, all subsequent messages contain + output_audio and output_audio_config. """ diff --git a/google/cloud/dialogflow_v2beta1/services/sessions/client.py b/google/cloud/dialogflow_v2beta1/services/sessions/client.py index 14689e32b..9800ee01b 100644 --- a/google/cloud/dialogflow_v2beta1/services/sessions/client.py +++ b/google/cloud/dialogflow_v2beta1/services/sessions/client.py @@ -124,6 +124,22 @@ def _get_default_mtls_endpoint(api_endpoint): DEFAULT_ENDPOINT ) + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SessionsClient: The constructed client. + """ + credentials = service_account.Credentials.from_service_account_info(info) + kwargs["credentials"] = credentials + return cls(*args, **kwargs) + @classmethod def from_service_account_file(cls, filename: str, *args, **kwargs): """Creates an instance of this client using the provided credentials @@ -136,7 +152,7 @@ def from_service_account_file(cls, filename: str, *args, **kwargs): kwargs: Additional arguments to pass to the constructor. Returns: - {@api.name}: The constructed client. + SessionsClient: The constructed client. """ credentials = service_account.Credentials.from_service_account_file(filename) kwargs["credentials"] = credentials @@ -304,10 +320,10 @@ def __init__( credentials identify the application to the service; if none are specified, the client will attempt to ascertain the credentials from the environment. - transport (Union[str, ~.SessionsTransport]): The + 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_options (google.api_core.client_options.ClientOptions): Custom options for the client. It won't take effect if a ``transport`` instance is provided. (1) The ``api_endpoint`` property can be used to override the default endpoint provided by the client. GOOGLE_API_USE_MTLS_ENDPOINT @@ -343,21 +359,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -400,7 +412,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -425,9 +437,9 @@ def detect_intent( environments `__. Args: - request (:class:`~.gcd_session.DetectIntentRequest`): + request (google.cloud.dialogflow_v2beta1.types.DetectIntentRequest): The request object. The request to detect user's intent. - session (:class:`str`): + session (str): Required. The name of the session this query is sent to. Supported formats: @@ -438,23 +450,26 @@ def detect_intent( If ``Location ID`` is not specified we assume default 'us' location. If ``Environment ID`` is not specified, - we assume default 'draft' environment. If ``User ID`` is - not specified, we are using "-". It's up to the API - caller to choose an appropriate ``Session ID`` and - ``User Id``. They can be a random number or some type of - user and session identifiers (preferably hashed). The - length of the ``Session ID`` and ``User ID`` must not - exceed 36 characters. For more information, see the `API + we assume default 'draft' environment + (``Environment ID`` might be referred to as environment + name at some places). If ``User ID`` is not specified, + we are using "-". It's up to the API caller to choose an + appropriate ``Session ID`` and ``User Id``. They can be + a random number or some type of user and session + identifiers (preferably hashed). The length of the + ``Session ID`` and ``User ID`` must not exceed 36 + characters. For more information, see the `API interactions guide `__. Note: Always use agent versions for production traffic. See `Versions and environments `__. + This corresponds to the ``session`` field on the ``request`` instance; if ``request`` is provided, this should not be set. - query_input (:class:`~.gcd_session.QueryInput`): + query_input (google.cloud.dialogflow_v2beta1.types.QueryInput): Required. The input specification. It can be set to: 1. an audio config @@ -465,6 +480,7 @@ def detect_intent( of text, or 3. an event that specifies which intent to trigger. + This corresponds to the ``query_input`` field on the ``request`` instance; if ``request`` is provided, this should not be set. @@ -476,7 +492,7 @@ def detect_intent( sent along with the request as metadata. Returns: - ~.gcd_session.DetectIntentResponse: + google.cloud.dialogflow_v2beta1.types.DetectIntentResponse: The message returned from the DetectIntent method. @@ -540,7 +556,7 @@ def streaming_detect_intent( environments `__. Args: - requests (Iterator[`~.session.StreamingDetectIntentRequest`]): + requests (Iterator[google.cloud.dialogflow_v2beta1.types.StreamingDetectIntentRequest]): The request object iterator. The top-level message sent by the client to the [Sessions.StreamingDetectIntent][google.cloud.dialogflow.v2beta1.Sessions.StreamingDetectIntent] @@ -584,28 +600,24 @@ def streaming_detect_intent( sent along with the request as metadata. Returns: - Iterable[~.session.StreamingDetectIntentResponse]: + Iterable[google.cloud.dialogflow_v2beta1.types.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 next message contains ``response_id``, - ``query_result``, ``alternative_query_results`` and - optionally ``webhook_status`` if a WebHook was - called. - - 3. If ``output_audio_config`` was specified in the - request or agent-level speech synthesizer is - configured, all subsequent messages contain - ``output_audio`` and ``output_audio_config``. + 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 next message contains response_id, + query_result, alternative_query_results and + optionally webhook_status if a WebHook was called. + 3. If output_audio_config was specified in the + request or agent-level speech synthesizer is + configured, all subsequent messages contain + output_audio and output_audio_config. """ diff --git a/google/cloud/dialogflow_v2beta1/services/sessions/transports/grpc.py b/google/cloud/dialogflow_v2beta1/services/sessions/transports/grpc.py index 9c2d9b711..43862fa89 100644 --- a/google/cloud/dialogflow_v2beta1/services/sessions/transports/grpc.py +++ b/google/cloud/dialogflow_v2beta1/services/sessions/transports/grpc.py @@ -61,6 +61,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -91,6 +92,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -107,6 +112,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -116,11 +126,6 @@ def __init__( 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 @@ -164,12 +169,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/services/sessions/transports/grpc_asyncio.py b/google/cloud/dialogflow_v2beta1/services/sessions/transports/grpc_asyncio.py index 9b9b80c6b..47502e9d5 100644 --- a/google/cloud/dialogflow_v2beta1/services/sessions/transports/grpc_asyncio.py +++ b/google/cloud/dialogflow_v2beta1/services/sessions/transports/grpc_asyncio.py @@ -105,6 +105,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -136,6 +137,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -152,6 +157,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -161,11 +171,6 @@ def __init__( 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 @@ -209,12 +214,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/dialogflow_v2beta1/types/__init__.py b/google/cloud/dialogflow_v2beta1/types/__init__.py index 40fad4187..924295b40 100644 --- a/google/cloud/dialogflow_v2beta1/types/__init__.py +++ b/google/cloud/dialogflow_v2beta1/types/__init__.py @@ -15,240 +15,412 @@ # limitations under the License. # +from .agent import ( + Agent, + DeleteAgentRequest, + ExportAgentRequest, + ExportAgentResponse, + GetAgentRequest, + GetValidationResultRequest, + ImportAgentRequest, + RestoreAgentRequest, + SearchAgentsRequest, + SearchAgentsResponse, + SetAgentRequest, + SubAgent, + TrainAgentRequest, +) +from .answer_record import ( + AgentAssistantFeedback, + AgentAssistantRecord, + AnswerFeedback, + AnswerRecord, + GetAnswerRecordRequest, + ListAnswerRecordsRequest, + ListAnswerRecordsResponse, + UpdateAnswerRecordRequest, +) from .audio_config import ( + InputAudioConfig, + OutputAudioConfig, SpeechContext, + SpeechToTextConfig, SpeechWordInfo, - InputAudioConfig, - VoiceSelectionParams, SynthesizeSpeechConfig, - OutputAudioConfig, TelephonyDtmfEvents, + VoiceSelectionParams, AudioEncoding, + OutputAudioEncoding, SpeechModelVariant, SsmlVoiceGender, - OutputAudioEncoding, TelephonyDtmf, ) -from .environment import ( - Environment, - ListEnvironmentsRequest, - ListEnvironmentsResponse, -) -from .validation_result import ( - ValidationError, - ValidationResult, -) -from .agent import ( - Agent, - GetAgentRequest, - SetAgentRequest, - DeleteAgentRequest, - SubAgent, - SearchAgentsRequest, - SearchAgentsResponse, - TrainAgentRequest, - ExportAgentRequest, - ExportAgentResponse, - ImportAgentRequest, - RestoreAgentRequest, - GetValidationResultRequest, -) from .context import ( Context, + CreateContextRequest, + DeleteAllContextsRequest, + DeleteContextRequest, + GetContextRequest, ListContextsRequest, ListContextsResponse, - GetContextRequest, - CreateContextRequest, UpdateContextRequest, - DeleteContextRequest, - DeleteAllContextsRequest, ) -from .gcs import GcsSource +from .conversation import ( + BatchCreateMessagesRequest, + BatchCreateMessagesResponse, + CallMatcher, + CompleteConversationRequest, + Conversation, + ConversationPhoneNumber, + CreateCallMatcherRequest, + CreateConversationRequest, + CreateMessageRequest, + DeleteCallMatcherRequest, + GetConversationRequest, + ListCallMatchersRequest, + ListCallMatchersResponse, + ListConversationsRequest, + ListConversationsResponse, + ListMessagesRequest, + ListMessagesResponse, +) +from .conversation_event import ConversationEvent +from .conversation_profile import ( + AutomatedAgentConfig, + ConversationProfile, + CreateConversationProfileRequest, + DeleteConversationProfileRequest, + GetConversationProfileRequest, + HumanAgentAssistantConfig, + HumanAgentHandoffConfig, + ListConversationProfilesRequest, + ListConversationProfilesResponse, + LoggingConfig, + NotificationConfig, + UpdateConversationProfileRequest, +) from .document import ( + CreateDocumentRequest, + DeleteDocumentRequest, Document, GetDocumentRequest, + ImportDocumentsRequest, + ImportDocumentsResponse, + ImportDocumentTemplate, + KnowledgeOperationMetadata, ListDocumentsRequest, ListDocumentsResponse, - CreateDocumentRequest, - DeleteDocumentRequest, - UpdateDocumentRequest, - KnowledgeOperationMetadata, ReloadDocumentRequest, + UpdateDocumentRequest, ) from .entity_type import ( + BatchCreateEntitiesRequest, + BatchDeleteEntitiesRequest, + BatchDeleteEntityTypesRequest, + BatchUpdateEntitiesRequest, + BatchUpdateEntityTypesRequest, + BatchUpdateEntityTypesResponse, + CreateEntityTypeRequest, + DeleteEntityTypeRequest, EntityType, + EntityTypeBatch, + GetEntityTypeRequest, ListEntityTypesRequest, ListEntityTypesResponse, - GetEntityTypeRequest, - CreateEntityTypeRequest, UpdateEntityTypeRequest, - DeleteEntityTypeRequest, - BatchUpdateEntityTypesRequest, - BatchUpdateEntityTypesResponse, - BatchDeleteEntityTypesRequest, - BatchCreateEntitiesRequest, - BatchUpdateEntitiesRequest, - BatchDeleteEntitiesRequest, - EntityTypeBatch, ) +from .environment import ( + Environment, + ListEnvironmentsRequest, + ListEnvironmentsResponse, +) +from .gcs import ( + GcsSource, + GcsSources, +) +from .human_agent_assistant_event import HumanAgentAssistantEvent from .intent import ( + BatchDeleteIntentsRequest, + BatchUpdateIntentsRequest, + BatchUpdateIntentsResponse, + CreateIntentRequest, + DeleteIntentRequest, + GetIntentRequest, Intent, + IntentBatch, ListIntentsRequest, ListIntentsResponse, - GetIntentRequest, - CreateIntentRequest, UpdateIntentRequest, - DeleteIntentRequest, - BatchUpdateIntentsRequest, - BatchUpdateIntentsResponse, - BatchDeleteIntentsRequest, - IntentBatch, IntentView, ) from .knowledge_base import ( + CreateKnowledgeBaseRequest, + DeleteKnowledgeBaseRequest, + GetKnowledgeBaseRequest, KnowledgeBase, ListKnowledgeBasesRequest, ListKnowledgeBasesResponse, - GetKnowledgeBaseRequest, - CreateKnowledgeBaseRequest, - DeleteKnowledgeBaseRequest, UpdateKnowledgeBaseRequest, ) -from .session_entity_type import ( - SessionEntityType, - ListSessionEntityTypesRequest, - ListSessionEntityTypesResponse, - GetSessionEntityTypeRequest, - CreateSessionEntityTypeRequest, - UpdateSessionEntityTypeRequest, - DeleteSessionEntityTypeRequest, +from .participant import ( + AnalyzeContentRequest, + AnalyzeContentResponse, + AnnotatedMessagePart, + ArticleAnswer, + AudioInput, + AutomatedAgentReply, + CompileSuggestionRequest, + CompileSuggestionResponse, + CreateParticipantRequest, + DtmfParameters, + FaqAnswer, + GetParticipantRequest, + InputAudio, + InputText, + InputTextConfig, + ListParticipantsRequest, + ListParticipantsResponse, + ListSuggestionsRequest, + ListSuggestionsResponse, + Message, + MessageAnnotation, + OutputAudio, + Participant, + ResponseMessage, + SmartReplyAnswer, + StreamingAnalyzeContentRequest, + StreamingAnalyzeContentResponse, + SuggestArticlesRequest, + SuggestArticlesResponse, + SuggestFaqAnswersRequest, + SuggestFaqAnswersResponse, + Suggestion, + SuggestionFeature, + SuggestionResult, + SuggestSmartRepliesRequest, + SuggestSmartRepliesResponse, + UpdateParticipantRequest, ) from .session import ( DetectIntentRequest, DetectIntentResponse, - QueryParameters, + EventInput, + KnowledgeAnswers, QueryInput, + QueryParameters, QueryResult, - KnowledgeAnswers, + Sentiment, + SentimentAnalysisRequestConfig, + SentimentAnalysisResult, StreamingDetectIntentRequest, StreamingDetectIntentResponse, StreamingRecognitionResult, TextInput, - EventInput, - SentimentAnalysisRequestConfig, - SentimentAnalysisResult, - Sentiment, +) +from .session_entity_type import ( + CreateSessionEntityTypeRequest, + DeleteSessionEntityTypeRequest, + GetSessionEntityTypeRequest, + ListSessionEntityTypesRequest, + ListSessionEntityTypesResponse, + SessionEntityType, + UpdateSessionEntityTypeRequest, +) +from .validation_result import ( + ValidationError, + ValidationResult, ) from .webhook import ( + OriginalDetectIntentRequest, WebhookRequest, WebhookResponse, - OriginalDetectIntentRequest, ) __all__ = ( + "Agent", + "DeleteAgentRequest", + "ExportAgentRequest", + "ExportAgentResponse", + "GetAgentRequest", + "GetValidationResultRequest", + "ImportAgentRequest", + "RestoreAgentRequest", + "SearchAgentsRequest", + "SearchAgentsResponse", + "SetAgentRequest", + "SubAgent", + "TrainAgentRequest", + "AgentAssistantFeedback", + "AgentAssistantRecord", + "AnswerFeedback", + "AnswerRecord", + "GetAnswerRecordRequest", + "ListAnswerRecordsRequest", + "ListAnswerRecordsResponse", + "UpdateAnswerRecordRequest", + "InputAudioConfig", + "OutputAudioConfig", "SpeechContext", + "SpeechToTextConfig", "SpeechWordInfo", - "InputAudioConfig", - "VoiceSelectionParams", "SynthesizeSpeechConfig", - "OutputAudioConfig", "TelephonyDtmfEvents", + "VoiceSelectionParams", "AudioEncoding", + "OutputAudioEncoding", "SpeechModelVariant", "SsmlVoiceGender", - "OutputAudioEncoding", "TelephonyDtmf", - "Environment", - "ListEnvironmentsRequest", - "ListEnvironmentsResponse", - "ValidationError", - "ValidationResult", - "Agent", - "GetAgentRequest", - "SetAgentRequest", - "DeleteAgentRequest", - "SubAgent", - "SearchAgentsRequest", - "SearchAgentsResponse", - "TrainAgentRequest", - "ExportAgentRequest", - "ExportAgentResponse", - "ImportAgentRequest", - "RestoreAgentRequest", - "GetValidationResultRequest", "Context", + "CreateContextRequest", + "DeleteAllContextsRequest", + "DeleteContextRequest", + "GetContextRequest", "ListContextsRequest", "ListContextsResponse", - "GetContextRequest", - "CreateContextRequest", "UpdateContextRequest", - "DeleteContextRequest", - "DeleteAllContextsRequest", - "GcsSource", + "BatchCreateMessagesRequest", + "BatchCreateMessagesResponse", + "CallMatcher", + "CompleteConversationRequest", + "Conversation", + "ConversationPhoneNumber", + "CreateCallMatcherRequest", + "CreateConversationRequest", + "CreateMessageRequest", + "DeleteCallMatcherRequest", + "GetConversationRequest", + "ListCallMatchersRequest", + "ListCallMatchersResponse", + "ListConversationsRequest", + "ListConversationsResponse", + "ListMessagesRequest", + "ListMessagesResponse", + "ConversationEvent", + "AutomatedAgentConfig", + "ConversationProfile", + "CreateConversationProfileRequest", + "DeleteConversationProfileRequest", + "GetConversationProfileRequest", + "HumanAgentAssistantConfig", + "HumanAgentHandoffConfig", + "ListConversationProfilesRequest", + "ListConversationProfilesResponse", + "LoggingConfig", + "NotificationConfig", + "UpdateConversationProfileRequest", + "CreateDocumentRequest", + "DeleteDocumentRequest", "Document", "GetDocumentRequest", + "ImportDocumentsRequest", + "ImportDocumentsResponse", + "ImportDocumentTemplate", + "KnowledgeOperationMetadata", "ListDocumentsRequest", "ListDocumentsResponse", - "CreateDocumentRequest", - "DeleteDocumentRequest", - "UpdateDocumentRequest", - "KnowledgeOperationMetadata", "ReloadDocumentRequest", + "UpdateDocumentRequest", + "BatchCreateEntitiesRequest", + "BatchDeleteEntitiesRequest", + "BatchDeleteEntityTypesRequest", + "BatchUpdateEntitiesRequest", + "BatchUpdateEntityTypesRequest", + "BatchUpdateEntityTypesResponse", + "CreateEntityTypeRequest", + "DeleteEntityTypeRequest", "EntityType", + "EntityTypeBatch", + "GetEntityTypeRequest", "ListEntityTypesRequest", "ListEntityTypesResponse", - "GetEntityTypeRequest", - "CreateEntityTypeRequest", "UpdateEntityTypeRequest", - "DeleteEntityTypeRequest", - "BatchUpdateEntityTypesRequest", - "BatchUpdateEntityTypesResponse", - "BatchDeleteEntityTypesRequest", - "BatchCreateEntitiesRequest", - "BatchUpdateEntitiesRequest", - "BatchDeleteEntitiesRequest", - "EntityTypeBatch", + "Environment", + "ListEnvironmentsRequest", + "ListEnvironmentsResponse", + "GcsSource", + "GcsSources", + "HumanAgentAssistantEvent", + "BatchDeleteIntentsRequest", + "BatchUpdateIntentsRequest", + "BatchUpdateIntentsResponse", + "CreateIntentRequest", + "DeleteIntentRequest", + "GetIntentRequest", "Intent", + "IntentBatch", "ListIntentsRequest", "ListIntentsResponse", - "GetIntentRequest", - "CreateIntentRequest", "UpdateIntentRequest", - "DeleteIntentRequest", - "BatchUpdateIntentsRequest", - "BatchUpdateIntentsResponse", - "BatchDeleteIntentsRequest", - "IntentBatch", "IntentView", + "CreateKnowledgeBaseRequest", + "DeleteKnowledgeBaseRequest", + "GetKnowledgeBaseRequest", "KnowledgeBase", "ListKnowledgeBasesRequest", "ListKnowledgeBasesResponse", - "GetKnowledgeBaseRequest", - "CreateKnowledgeBaseRequest", - "DeleteKnowledgeBaseRequest", "UpdateKnowledgeBaseRequest", - "SessionEntityType", - "ListSessionEntityTypesRequest", - "ListSessionEntityTypesResponse", - "GetSessionEntityTypeRequest", - "CreateSessionEntityTypeRequest", - "UpdateSessionEntityTypeRequest", - "DeleteSessionEntityTypeRequest", + "AnalyzeContentRequest", + "AnalyzeContentResponse", + "AnnotatedMessagePart", + "ArticleAnswer", + "AudioInput", + "AutomatedAgentReply", + "CompileSuggestionRequest", + "CompileSuggestionResponse", + "CreateParticipantRequest", + "DtmfParameters", + "FaqAnswer", + "GetParticipantRequest", + "InputAudio", + "InputText", + "InputTextConfig", + "ListParticipantsRequest", + "ListParticipantsResponse", + "ListSuggestionsRequest", + "ListSuggestionsResponse", + "Message", + "MessageAnnotation", + "OutputAudio", + "Participant", + "ResponseMessage", + "SmartReplyAnswer", + "StreamingAnalyzeContentRequest", + "StreamingAnalyzeContentResponse", + "SuggestArticlesRequest", + "SuggestArticlesResponse", + "SuggestFaqAnswersRequest", + "SuggestFaqAnswersResponse", + "Suggestion", + "SuggestionFeature", + "SuggestionResult", + "SuggestSmartRepliesRequest", + "SuggestSmartRepliesResponse", + "UpdateParticipantRequest", "DetectIntentRequest", "DetectIntentResponse", - "QueryParameters", + "EventInput", + "KnowledgeAnswers", "QueryInput", + "QueryParameters", "QueryResult", - "KnowledgeAnswers", + "Sentiment", + "SentimentAnalysisRequestConfig", + "SentimentAnalysisResult", "StreamingDetectIntentRequest", "StreamingDetectIntentResponse", "StreamingRecognitionResult", "TextInput", - "EventInput", - "SentimentAnalysisRequestConfig", - "SentimentAnalysisResult", - "Sentiment", + "CreateSessionEntityTypeRequest", + "DeleteSessionEntityTypeRequest", + "GetSessionEntityTypeRequest", + "ListSessionEntityTypesRequest", + "ListSessionEntityTypesResponse", + "SessionEntityType", + "UpdateSessionEntityTypeRequest", + "ValidationError", + "ValidationResult", + "OriginalDetectIntentRequest", "WebhookRequest", "WebhookResponse", - "OriginalDetectIntentRequest", ) diff --git a/google/cloud/dialogflow_v2beta1/types/agent.py b/google/cloud/dialogflow_v2beta1/types/agent.py index 5a20e4115..980d7cb91 100644 --- a/google/cloud/dialogflow_v2beta1/types/agent.py +++ b/google/cloud/dialogflow_v2beta1/types/agent.py @@ -86,7 +86,7 @@ class Agent(proto.Message): enable_logging (bool): Optional. Determines whether this agent should log conversation queries. - match_mode (~.gcd_agent.Agent.MatchMode): + match_mode (google.cloud.dialogflow_v2beta1.types.Agent.MatchMode): Optional. Determines how intents are detected from user queries. classification_threshold (float): @@ -101,14 +101,14 @@ class Agent(proto.Message): values range from 0.0 (completely uncertain) to 1.0 (completely certain). If set to 0.0, the default of 0.3 is used. - api_version (~.gcd_agent.Agent.ApiVersion): + api_version (google.cloud.dialogflow_v2beta1.types.Agent.ApiVersion): Optional. API version displayed in Dialogflow console. If not specified, V2 API is assumed. Clients are free to query different service endpoints for different API versions. However, bots connectors and webhook calls will follow the specified API version. - tier (~.gcd_agent.Agent.Tier): + tier (google.cloud.dialogflow_v2beta1.types.Agent.Tier): Optional. The agent tier. If not specified, TIER_STANDARD is assumed. """ @@ -179,9 +179,9 @@ class SetAgentRequest(proto.Message): [Agents.SetAgent][google.cloud.dialogflow.v2beta1.Agents.SetAgent]. Attributes: - agent (~.gcd_agent.Agent): + agent (google.cloud.dialogflow_v2beta1.types.Agent): Required. The agent to update. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ @@ -254,7 +254,7 @@ class SearchAgentsResponse(proto.Message): [Agents.SearchAgents][google.cloud.dialogflow.v2beta1.Agents.SearchAgents]. Attributes: - agents (Sequence[~.gcd_agent.Agent]): + agents (Sequence[google.cloud.dialogflow_v2beta1.types.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): diff --git a/google/cloud/dialogflow_v2beta1/types/answer_record.py b/google/cloud/dialogflow_v2beta1/types/answer_record.py new file mode 100644 index 000000000..3661084a4 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/types/answer_record.py @@ -0,0 +1,337 @@ +# -*- 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.dialogflow_v2beta1.types import participant +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.v2beta1", + manifest={ + "AnswerRecord", + "AgentAssistantRecord", + "AnswerFeedback", + "AgentAssistantFeedback", + "GetAnswerRecordRequest", + "ListAnswerRecordsRequest", + "ListAnswerRecordsResponse", + "UpdateAnswerRecordRequest", + }, +) + + +class AnswerRecord(proto.Message): + r"""Answer records are records to manage answer history and feedbacks + for Dialogflow. + + Currently, answer record includes: + + - human agent assistant article suggestion + - human agent assistant faq article + + It doesn't include: + + - ``DetectIntent`` intent matching + - ``DetectIntent`` knowledge + + Answer records are not related to the conversation history in the + Dialogflow Console. A Record is generated even when the end-user + disables conversation history in the console. Records are created + when there's a human agent assistant suggestion generated. + + A typical workflow for customers provide feedback to an answer is: + + 1. For human agent assistant, customers get suggestion via + ListSuggestions API. Together with the answers, + [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] + are returned to the customers. + 2. The customer uses the + [AnswerRecord.name][google.cloud.dialogflow.v2beta1.AnswerRecord.name] + to call the [UpdateAnswerRecord][] method to send feedback about + a specific answer that they believe is wrong. + + Attributes: + name (str): + The unique identifier of this answer record. Required for + [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.UpdateAnswerRecord] + method. Format: + ``projects//locations//answerRecords/``. + answer_feedback (google.cloud.dialogflow_v2beta1.types.AnswerFeedback): + Optional. The AnswerFeedback for this record. You can set + this with + [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.UpdateAnswerRecord] + in order to give us feedback about this answer. + agent_assistant_record (google.cloud.dialogflow_v2beta1.types.AgentAssistantRecord): + Output only. The record for human agent + assistant. + """ + + name = proto.Field(proto.STRING, number=1) + + answer_feedback = proto.Field(proto.MESSAGE, number=3, message="AnswerFeedback",) + + agent_assistant_record = proto.Field( + proto.MESSAGE, number=4, oneof="record", message="AgentAssistantRecord", + ) + + +class AgentAssistantRecord(proto.Message): + r"""Represents a record of a human agent assistant answer. + + Attributes: + article_suggestion_answer (google.cloud.dialogflow_v2beta1.types.ArticleAnswer): + Output only. The article suggestion answer. + faq_answer (google.cloud.dialogflow_v2beta1.types.FaqAnswer): + Output only. The FAQ answer. + """ + + article_suggestion_answer = proto.Field( + proto.MESSAGE, number=5, oneof="answer", message=participant.ArticleAnswer, + ) + + faq_answer = proto.Field( + proto.MESSAGE, number=6, oneof="answer", message=participant.FaqAnswer, + ) + + +class AnswerFeedback(proto.Message): + r"""Represents feedback the customer has about the quality & + correctness of a certain answer in a conversation. + + Attributes: + correctness_level (google.cloud.dialogflow_v2beta1.types.AnswerFeedback.CorrectnessLevel): + The correctness level of the specific answer. + agent_assistant_detail_feedback (google.cloud.dialogflow_v2beta1.types.AgentAssistantFeedback): + Optional. Detail feedback of agent assistant + suggestions. + clicked (bool): + Indicates whether the answer/item was clicked + by the human agent or not. Default to false. + click_time (google.protobuf.timestamp_pb2.Timestamp): + Time when the answer/item was clicked. + displayed (bool): + Indicates whether the answer/item was + displayed to the human agent in the agent + desktop UI. Default to false. + display_time (google.protobuf.timestamp_pb2.Timestamp): + Time when the answer/item was displayed. + """ + + class CorrectnessLevel(proto.Enum): + r"""The correctness level of an answer.""" + CORRECTNESS_LEVEL_UNSPECIFIED = 0 + NOT_CORRECT = 1 + PARTIALLY_CORRECT = 2 + FULLY_CORRECT = 3 + + correctness_level = proto.Field(proto.ENUM, number=1, enum=CorrectnessLevel,) + + agent_assistant_detail_feedback = proto.Field( + proto.MESSAGE, + number=2, + oneof="detail_feedback", + message="AgentAssistantFeedback", + ) + + clicked = proto.Field(proto.BOOL, number=3) + + click_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) + + displayed = proto.Field(proto.BOOL, number=4) + + display_time = proto.Field(proto.MESSAGE, number=6, message=timestamp.Timestamp,) + + +class AgentAssistantFeedback(proto.Message): + r"""Detail feedback of Agent Assistant result. + + Attributes: + answer_relevance (google.cloud.dialogflow_v2beta1.types.AgentAssistantFeedback.AnswerRelevance): + Optional. Whether or not the suggested answer is relevant. + + For example: + + - Query: "Can I change my mailing address?" + - Suggested document says: "Items must be + returned/exchanged within 60 days of the purchase date." + - [answer_relevance][google.cloud.dialogflow.v2beta1.AgentAssistantFeedback.answer_relevance]: + [AnswerRelevance.IRRELEVANT][google.cloud.dialogflow.v2beta1.AgentAssistantFeedback.AnswerRelevance.IRRELEVANT] + document_correctness (google.cloud.dialogflow_v2beta1.types.AgentAssistantFeedback.DocumentCorrectness): + Optional. Whether or not the information in the document is + correct. + + For example: + + - Query: "Can I return the package in 2 days once + received?" + - Suggested document says: "Items must be + returned/exchanged within 60 days of the purchase date." + - Ground truth: "No return or exchange is allowed." + - + document_efficiency (google.cloud.dialogflow_v2beta1.types.AgentAssistantFeedback.DocumentEfficiency): + Optional. Whether or not the suggested document is + efficient. For example, if the document is poorly written, + hard to understand, hard to use or too long to find useful + information, + [document_efficiency][google.cloud.dialogflow.v2beta1.AgentAssistantFeedback.document_efficiency] + is + [DocumentEfficiency.INEFFICIENT][google.cloud.dialogflow.v2beta1.AgentAssistantFeedback.DocumentEfficiency.INEFFICIENT]. + summarization_feedback (google.cloud.dialogflow_v2beta1.types.AgentAssistantFeedback.SummarizationFeedback): + Feedback for conversation summarization. + """ + + class AnswerRelevance(proto.Enum): + r"""Relevance of an answer.""" + ANSWER_RELEVANCE_UNSPECIFIED = 0 + IRRELEVANT = 1 + RELEVANT = 2 + + class DocumentCorrectness(proto.Enum): + r"""Correctness of document.""" + DOCUMENT_CORRECTNESS_UNSPECIFIED = 0 + INCORRECT = 1 + CORRECT = 2 + + class DocumentEfficiency(proto.Enum): + r"""Efficiency of document.""" + DOCUMENT_EFFICIENCY_UNSPECIFIED = 0 + INEFFICIENT = 1 + EFFICIENT = 2 + + class SummarizationFeedback(proto.Message): + r"""Feedback for conversation summarization. + + Attributes: + start_timestamp (google.protobuf.timestamp_pb2.Timestamp): + Timestamp when composing of the summary + starts. + submit_timestamp (google.protobuf.timestamp_pb2.Timestamp): + Timestamp when the summary was submitted. + summary_text (str): + Text of actual submitted summary. + """ + + start_timestamp = proto.Field( + proto.MESSAGE, number=1, message=timestamp.Timestamp, + ) + + submit_timestamp = proto.Field( + proto.MESSAGE, number=2, message=timestamp.Timestamp, + ) + + summary_text = proto.Field(proto.STRING, number=3) + + answer_relevance = proto.Field(proto.ENUM, number=1, enum=AnswerRelevance,) + + document_correctness = proto.Field(proto.ENUM, number=2, enum=DocumentCorrectness,) + + document_efficiency = proto.Field(proto.ENUM, number=3, enum=DocumentEfficiency,) + + summarization_feedback = proto.Field( + proto.MESSAGE, number=4, message=SummarizationFeedback, + ) + + +class GetAnswerRecordRequest(proto.Message): + r"""Request message for + [AnswerRecords.GetAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.GetAnswerRecord]. + + Attributes: + name (str): + Required. The name of the answer record to retrieve. Format: + ``projects//locations//answerRecords/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class ListAnswerRecordsRequest(proto.Message): + r"""Request message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecords.ListAnswerRecords]. + + Attributes: + parent (str): + Required. The project to list all answer records for in + reverse chronological order. Format: + ``projects//locations/``. + page_size (int): + Optional. The maximum number of records to + return in a single page. The server may return + fewer records than this. If unspecified, we use + 10. The maximum is 100. + page_token (str): + Optional. The + [ListAnswerRecordsResponse.next_page_token][google.cloud.dialogflow.v2beta1.ListAnswerRecordsResponse.next_page_token] + value returned from a previous list request used to continue + listing on the next page. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=3) + + page_token = proto.Field(proto.STRING, number=4) + + +class ListAnswerRecordsResponse(proto.Message): + r"""Response message for + [AnswerRecords.ListAnswerRecords][google.cloud.dialogflow.v2beta1.AnswerRecords.ListAnswerRecords]. + + Attributes: + answer_records (Sequence[google.cloud.dialogflow_v2beta1.types.AnswerRecord]): + The list of answer records. + next_page_token (str): + A token to retrieve next page of results. Or empty if there + are no more results. Pass this value in the + [ListAnswerRecordsRequest.page_token][google.cloud.dialogflow.v2beta1.ListAnswerRecordsRequest.page_token] + field in the subsequent call to ``ListAnswerRecords`` method + to retrieve the next page of results. + """ + + @property + def raw_page(self): + return self + + answer_records = proto.RepeatedField( + proto.MESSAGE, number=1, message="AnswerRecord", + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class UpdateAnswerRecordRequest(proto.Message): + r"""Request message for + [AnswerRecords.UpdateAnswerRecord][google.cloud.dialogflow.v2beta1.AnswerRecords.UpdateAnswerRecord]. + + Attributes: + answer_record (google.cloud.dialogflow_v2beta1.types.AnswerRecord): + Required. Answer record to update. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. The mask to control which fields + get updated. + """ + + answer_record = proto.Field(proto.MESSAGE, number=1, message="AnswerRecord",) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2beta1/types/audio_config.py b/google/cloud/dialogflow_v2beta1/types/audio_config.py index 416ae8412..5fffbd881 100644 --- a/google/cloud/dialogflow_v2beta1/types/audio_config.py +++ b/google/cloud/dialogflow_v2beta1/types/audio_config.py @@ -36,6 +36,7 @@ "SynthesizeSpeechConfig", "OutputAudioConfig", "TelephonyDtmfEvents", + "SpeechToTextConfig", }, ) @@ -161,12 +162,12 @@ class SpeechWordInfo(proto.Message): Attributes: word (str): The word this info is for. - start_offset (~.duration.Duration): + start_offset (google.protobuf.duration_pb2.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): + end_offset (google.protobuf.duration_pb2.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 @@ -198,7 +199,7 @@ class InputAudioConfig(proto.Message): content. Attributes: - audio_encoding (~.audio_config.AudioEncoding): + audio_encoding (google.cloud.dialogflow_v2beta1.types.AudioEncoding): Required. Audio encoding of the audio content to process. sample_rate_hertz (int): @@ -234,7 +235,7 @@ class InputAudioConfig(proto.Message): `speech_contexts <>`__, Dialogflow will treat the `phrase_hints <>`__ as a single additional `SpeechContext <>`__. - speech_contexts (Sequence[~.audio_config.SpeechContext]): + speech_contexts (Sequence[google.cloud.dialogflow_v2beta1.types.SpeechContext]): Context information to assist speech recognition. See `the Cloud Speech @@ -252,7 +253,7 @@ class InputAudioConfig(proto.Message): Speech API documentation `__ for more details. - model_variant (~.audio_config.SpeechModelVariant): + model_variant (google.cloud.dialogflow_v2beta1.types.SpeechModelVariant): Which variant of the [Speech model][google.cloud.dialogflow.v2beta1.InputAudioConfig.model] to use. @@ -267,6 +268,13 @@ class InputAudioConfig(proto.Message): only for streaming methods. Note: When specified, InputAudioConfig.single_utterance takes precedence over StreamingDetectIntentRequest.single_utterance. + disable_no_speech_recognized_event (bool): + Only used in + [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent] + and + [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.StreamingAnalyzeContent]. + If ``false`` and recognition doesn't return any result, + trigger ``NO_SPEECH_RECOGNIZED`` event to Dialogflow agent. """ audio_encoding = proto.Field(proto.ENUM, number=1, enum="AudioEncoding",) @@ -289,6 +297,8 @@ class InputAudioConfig(proto.Message): single_utterance = proto.Field(proto.BOOL, number=8) + disable_no_speech_recognized_event = proto.Field(proto.BOOL, number=14) + class VoiceSelectionParams(proto.Message): r"""Description of which voice to use for speech synthesis. @@ -299,7 +309,11 @@ class VoiceSelectionParams(proto.Message): will choose a voice based on the other parameters such as language_code and [ssml_gender][google.cloud.dialogflow.v2beta1.VoiceSelectionParams.ssml_gender]. - ssml_gender (~.audio_config.SsmlVoiceGender): + + For the list of available voices, please refer to `Supported + voices and + languages `__. + ssml_gender (google.cloud.dialogflow_v2beta1.types.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 @@ -346,7 +360,7 @@ class SynthesizeSpeechConfig(proto.Message): synthesized) text to speech. Effects are applied on top of each other in the order they are given. - voice (~.audio_config.VoiceSelectionParams): + voice (google.cloud.dialogflow_v2beta1.types.VoiceSelectionParams): Optional. The desired voice of the synthesized audio. """ @@ -369,7 +383,7 @@ class OutputAudioConfig(proto.Message): agent. Attributes: - audio_encoding (~.audio_config.OutputAudioEncoding): + audio_encoding (google.cloud.dialogflow_v2beta1.types.OutputAudioEncoding): Required. Audio encoding of the synthesized audio content. sample_rate_hertz (int): @@ -381,7 +395,7 @@ class OutputAudioConfig(proto.Message): 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): + synthesize_speech_config (google.cloud.dialogflow_v2beta1.types.SynthesizeSpeechConfig): Configuration of how speech should be synthesized. """ @@ -399,11 +413,29 @@ class TelephonyDtmfEvents(proto.Message): r"""A wrapper of repeated TelephonyDtmf digits. Attributes: - dtmf_events (Sequence[~.audio_config.TelephonyDtmf]): + dtmf_events (Sequence[google.cloud.dialogflow_v2beta1.types.TelephonyDtmf]): A sequence of TelephonyDtmf digits. """ dtmf_events = proto.RepeatedField(proto.ENUM, number=1, enum="TelephonyDtmf",) +class SpeechToTextConfig(proto.Message): + r"""Configures speech transcription for + [ConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfile]. + + Attributes: + speech_model_variant (google.cloud.dialogflow_v2beta1.types.SpeechModelVariant): + Optional. The speech model used in speech to text. + ``SPEECH_MODEL_VARIANT_UNSPECIFIED``, ``USE_BEST_AVAILABLE`` + will be treated as ``USE_ENHANCED``. It can be overridden in + [AnalyzeContentRequest][google.cloud.dialogflow.v2beta1.AnalyzeContentRequest] + and + [StreamingAnalyzeContentRequest][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest] + request. + """ + + speech_model_variant = proto.Field(proto.ENUM, number=1, enum="SpeechModelVariant",) + + __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2beta1/types/context.py b/google/cloud/dialogflow_v2beta1/types/context.py index cf3f152ee..d0179a6a2 100644 --- a/google/cloud/dialogflow_v2beta1/types/context.py +++ b/google/cloud/dialogflow_v2beta1/types/context.py @@ -86,7 +86,7 @@ class Context(proto.Message): ``0``, the context expires immediately. Contexts expire automatically after 20 minutes if there are no matching queries. - parameters (~.struct.Struct): + parameters (google.protobuf.struct_pb2.Struct): Optional. The collection of parameters associated with this context. Depending on your protocol or client library @@ -98,9 +98,10 @@ class Context(proto.Message): - 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: + composite entity: map - Else: depending on + parameter value type, could be one of string, + number, boolean, null, list or map + - MapValue value: - If parameter's entity type is a composite entity: map from composite entity property names to property values - @@ -153,7 +154,7 @@ class ListContextsResponse(proto.Message): [Contexts.ListContexts][google.cloud.dialogflow.v2beta1.Contexts.ListContexts]. Attributes: - contexts (Sequence[~.gcd_context.Context]): + contexts (Sequence[google.cloud.dialogflow_v2beta1.types.Context]): The list of contexts. There will be a maximum number of items returned based on the page_size field in the request. next_page_token (str): @@ -211,7 +212,7 @@ class CreateContextRequest(proto.Message): location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. - context (~.gcd_context.Context): + context (google.cloud.dialogflow_v2beta1.types.Context): Required. The context to create. """ @@ -225,9 +226,9 @@ class UpdateContextRequest(proto.Message): [Contexts.UpdateContext][google.cloud.dialogflow.v2beta1.Contexts.UpdateContext]. Attributes: - context (~.gcd_context.Context): + context (google.cloud.dialogflow_v2beta1.types.Context): Required. The context to update. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ diff --git a/google/cloud/dialogflow_v2beta1/types/conversation.py b/google/cloud/dialogflow_v2beta1/types/conversation.py new file mode 100644 index 000000000..4fe64ebf2 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/types/conversation.py @@ -0,0 +1,558 @@ +# -*- 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.dialogflow_v2beta1.types import participant +from google.protobuf import timestamp_pb2 as timestamp # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.v2beta1", + manifest={ + "Conversation", + "ConversationPhoneNumber", + "CallMatcher", + "CreateConversationRequest", + "ListConversationsRequest", + "ListConversationsResponse", + "GetConversationRequest", + "CompleteConversationRequest", + "CreateCallMatcherRequest", + "ListCallMatchersRequest", + "ListCallMatchersResponse", + "DeleteCallMatcherRequest", + "CreateMessageRequest", + "BatchCreateMessagesRequest", + "BatchCreateMessagesResponse", + "ListMessagesRequest", + "ListMessagesResponse", + }, +) + + +class Conversation(proto.Message): + r"""Represents a conversation. + A conversation is an interaction between an agent, including + live agents and Dialogflow agents, and a support customer. + Conversations can include phone calls and text-based chat + sessions. + + Attributes: + name (str): + Output only. The unique identifier of this conversation. + Format: + ``projects//locations//conversations/``. + lifecycle_state (google.cloud.dialogflow_v2beta1.types.Conversation.LifecycleState): + Output only. The current state of the + Conversation. + conversation_profile (str): + Required. The Conversation Profile to be used to configure + this Conversation. This field cannot be updated. Format: + ``projects//locations//conversationProfiles/``. + phone_number (google.cloud.dialogflow_v2beta1.types.ConversationPhoneNumber): + Output only. Required if the conversation is + to be connected over telephony. + conversation_stage (google.cloud.dialogflow_v2beta1.types.Conversation.ConversationStage): + The stage of a conversation. It indicates whether the + virtual agent or a human agent is handling the conversation. + + If the conversation is created with the conversation profile + that has Dialogflow config set, defaults to + [ConversationStage.VIRTUAL_AGENT_STAGE][google.cloud.dialogflow.v2beta1.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE]; + Otherwise, defaults to + [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2beta1.Conversation.ConversationStage.HUMAN_ASSIST_STAGE]. + + If the conversation is created with the conversation profile + that has Dialogflow config set but explicitly sets + conversation_stage to + [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2beta1.Conversation.ConversationStage.HUMAN_ASSIST_STAGE], + it skips + [ConversationStage.VIRTUAL_AGENT_STAGE][google.cloud.dialogflow.v2beta1.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE] + stage and directly goes to + [ConversationStage.HUMAN_ASSIST_STAGE][google.cloud.dialogflow.v2beta1.Conversation.ConversationStage.HUMAN_ASSIST_STAGE]. + start_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The time the conversation was + started. + end_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The time the conversation was + finished. + """ + + class LifecycleState(proto.Enum): + r"""Enumeration of the completion status of the conversation.""" + LIFECYCLE_STATE_UNSPECIFIED = 0 + IN_PROGRESS = 1 + COMPLETED = 2 + + class ConversationStage(proto.Enum): + r"""Enumeration of the different conversation stages a + conversation can be in. Reference: + https://cloud.google.com/dialogflow/priv/docs/contact- + center/basics#stages + """ + CONVERSATION_STAGE_UNSPECIFIED = 0 + VIRTUAL_AGENT_STAGE = 1 + HUMAN_ASSIST_STAGE = 2 + + name = proto.Field(proto.STRING, number=1) + + lifecycle_state = proto.Field(proto.ENUM, number=2, enum=LifecycleState,) + + conversation_profile = proto.Field(proto.STRING, number=3) + + phone_number = proto.Field( + proto.MESSAGE, number=4, message="ConversationPhoneNumber", + ) + + conversation_stage = proto.Field(proto.ENUM, number=7, enum=ConversationStage,) + + start_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) + + end_time = proto.Field(proto.MESSAGE, number=6, message=timestamp.Timestamp,) + + +class ConversationPhoneNumber(proto.Message): + r"""Represents a phone number for telephony integration. It + allows for connecting a particular conversation over telephony. + + Attributes: + phone_number (str): + Output only. The phone number to connect to + this conversation. + """ + + phone_number = proto.Field(proto.STRING, number=3) + + +class CallMatcher(proto.Message): + r"""Represents a call matcher that describes criteria for matching + incoming SIP calls to a conversation. When Dialogflow get a SIP call + from a third-party carrier, Dialogflow matches the call to an + existing conversation by either: + + - Extracting the conversation id from the `Call-Info + header `__, + e.g. + ``Call-Info: ;purpose=Goog-ContactCenter-Conversation``. + - Or, if that doesn't work, matching incoming `SIP + headers `__ + against any + [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] for + the conversation. + + If an incoming SIP call without valid ``Call-Info`` header matches + to zero or multiple conversations with ``CallMatcher``, we reject + it. + + A call matcher contains equality conditions for SIP headers that all + have to be fulfilled in order for a SIP call to match. + + The matched SIP headers consist of well-known headers (``To``, + ``From``, ``Call-ID``) and custom headers. A + [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] is only + valid if it specifies: + + - At least 1 custom header, + - or at least 2 well-known headers. + + Attributes: + name (str): + Output only. The unique identifier of this call matcher. + Format: + ``projects//locations//conversations//callMatchers/``. + to_header (str): + Value of the ```To`` + header `__ + to match. If empty or unspecified, we don't match to the + ```To`` + header `__. + from_header (str): + Value of the ```From`` + header `__ + to match. If empty or unspecified, we don't match to the + ```From`` + header `__. + call_id_header (str): + Value of the ```Call-ID`` + header `__ + to match. If empty or unspecified, we don't match to the + ```Call-ID`` + header `__. + custom_headers (google.cloud.dialogflow_v2beta1.types.CallMatcher.CustomHeaders): + Custom SIP headers that must match. + """ + + class CustomHeaders(proto.Message): + r"""Custom SIP headers. See the `description of headers in the + RFC `__. + + Attributes: + cisco_guid (str): + Cisco's proprietary ``Cisco-Guid`` header. + """ + + cisco_guid = proto.Field(proto.STRING, number=1) + + name = proto.Field(proto.STRING, number=1) + + to_header = proto.Field(proto.STRING, number=2) + + from_header = proto.Field(proto.STRING, number=3) + + call_id_header = proto.Field(proto.STRING, number=4) + + custom_headers = proto.Field(proto.MESSAGE, number=5, message=CustomHeaders,) + + +class CreateConversationRequest(proto.Message): + r"""The request message for + [Conversations.CreateConversation][google.cloud.dialogflow.v2beta1.Conversations.CreateConversation]. + + Attributes: + parent (str): + Required. Resource identifier of the project creating the + conversation. Format: + ``projects//locations/``. + conversation (google.cloud.dialogflow_v2beta1.types.Conversation): + Required. The conversation to create. + conversation_id (str): + Optional. Identifier of the conversation. Generally it's + auto generated by Google. Only set it if you cannot wait for + the response to return a auto-generated one to you. + + The conversation ID must be compliant with the regression + fomula "[a-zA-Z][a-zA-Z0-9_-]*" with the characters length + in range of [3,64]. If the field is provided, the caller is + resposible for + + 1. the uniqueness of the ID, otherwise the request will be + rejected. + 2. the consistency for whether to use custom ID or not under + a project to better ensure uniqueness. + """ + + parent = proto.Field(proto.STRING, number=1) + + conversation = proto.Field(proto.MESSAGE, number=2, message="Conversation",) + + conversation_id = proto.Field(proto.STRING, number=3) + + +class ListConversationsRequest(proto.Message): + r"""The request message for + [Conversations.ListConversations][google.cloud.dialogflow.v2beta1.Conversations.ListConversations]. + + Attributes: + parent (str): + Required. The project from which to list all conversation. + Format: ``projects//locations/``. + page_size (int): + Optional. The maximum number of items to + return in a single page. By default 100 and at + most 1000. + page_token (str): + Optional. The next_page_token value returned from a previous + list request. + filter (str): + A filter expression that filters conversations listed in the + response. In general, the expression must specify the field + name, a comparison operator, and the value to use for + filtering: + + .. raw:: html + +
    +
  • The value must be a string, a number, or a boolean.
  • +
  • The comparison operator must be either `=`,`!=`, `>`, or `<`.
  • +
  • To filter on multiple expressions, separate the + expressions with `AND` or `OR` (omitting both implies `AND`).
  • +
  • For clarity, expressions can be enclosed in parentheses.
  • +
+ Only `lifecycle_state` can be filtered on in this way. For example, + the following expression only returns `COMPLETED` conversations: + + ``lifecycle_state = "COMPLETED"`` + + For more information about filtering, see `API + Filtering `__. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + filter = proto.Field(proto.STRING, number=4) + + +class ListConversationsResponse(proto.Message): + r"""The response message for + [Conversations.ListConversations][google.cloud.dialogflow.v2beta1.Conversations.ListConversations]. + + Attributes: + conversations (Sequence[google.cloud.dialogflow_v2beta1.types.Conversation]): + The list of conversations. 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 + + conversations = proto.RepeatedField( + proto.MESSAGE, number=1, message="Conversation", + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetConversationRequest(proto.Message): + r"""The request message for + [Conversations.GetConversation][google.cloud.dialogflow.v2beta1.Conversations.GetConversation]. + + Attributes: + name (str): + Required. The name of the conversation. Format: + ``projects//locations//conversations/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CompleteConversationRequest(proto.Message): + r"""The request message for + [Conversations.CompleteConversation][google.cloud.dialogflow.v2beta1.Conversations.CompleteConversation]. + + Attributes: + name (str): + Required. Resource identifier of the conversation to close. + Format: + ``projects//locations//conversations/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreateCallMatcherRequest(proto.Message): + r"""The request message for + [Conversations.CreateCallMatcher][google.cloud.dialogflow.v2beta1.Conversations.CreateCallMatcher]. + + Attributes: + parent (str): + Required. Resource identifier of the conversation adding the + call matcher. Format: + ``projects//locations//conversations/``. + call_matcher (google.cloud.dialogflow_v2beta1.types.CallMatcher): + Required. The call matcher to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + call_matcher = proto.Field(proto.MESSAGE, number=2, message="CallMatcher",) + + +class ListCallMatchersRequest(proto.Message): + r"""The request message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2beta1.Conversations.ListCallMatchers]. + + Attributes: + parent (str): + Required. The conversation to list all call matchers from. + Format: + ``projects//locations//conversations/``. + page_size (int): + Optional. The maximum number of items to + return in a single page. By default 100 and at + most 1000. + page_token (str): + Optional. 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 ListCallMatchersResponse(proto.Message): + r"""The response message for + [Conversations.ListCallMatchers][google.cloud.dialogflow.v2beta1.Conversations.ListCallMatchers]. + + Attributes: + call_matchers (Sequence[google.cloud.dialogflow_v2beta1.types.CallMatcher]): + The list of call matchers. There is 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 + + call_matchers = proto.RepeatedField(proto.MESSAGE, number=1, message="CallMatcher",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class DeleteCallMatcherRequest(proto.Message): + r"""The request message for + [Conversations.DeleteCallMatcher][google.cloud.dialogflow.v2beta1.Conversations.DeleteCallMatcher]. + + Attributes: + name (str): + Required. The unique identifier of the + [CallMatcher][google.cloud.dialogflow.v2beta1.CallMatcher] + to delete. Format: + ``projects//locations//conversations//callMatchers/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreateMessageRequest(proto.Message): + r"""The request message to create one Message. Currently it is + only used in BatchCreateMessagesRequest. + + Attributes: + parent (str): + Required. Resource identifier of the conversation to create + message. Format: + ``projects//locations//conversations/``. + message (google.cloud.dialogflow_v2beta1.types.Message): + Required. The message to create. + [Message.participant][google.cloud.dialogflow.v2beta1.Message.participant] + is required. + """ + + parent = proto.Field(proto.STRING, number=1) + + message = proto.Field(proto.MESSAGE, number=2, message=participant.Message,) + + +class BatchCreateMessagesRequest(proto.Message): + r"""The request message for + [Conversations.BatchCreateMessagesRequest][]. + + Attributes: + parent (str): + Required. Resource identifier of the conversation to create + message. Format: + ``projects//locations//conversations/``. + requests (Sequence[google.cloud.dialogflow_v2beta1.types.CreateMessageRequest]): + Required. A maximum of 1000 Messages can be created in a + batch. [CreateMessageRequest.message.send_time][] is + required. All created messages will have identical + [Message.create_time][google.cloud.dialogflow.v2beta1.Message.create_time]. + """ + + parent = proto.Field(proto.STRING, number=1) + + requests = proto.RepeatedField( + proto.MESSAGE, number=2, message="CreateMessageRequest", + ) + + +class BatchCreateMessagesResponse(proto.Message): + r"""The request message for + [Conversations.BatchCreateMessagesResponse][]. + + Attributes: + messages (Sequence[google.cloud.dialogflow_v2beta1.types.Message]): + Messages created. + """ + + messages = proto.RepeatedField( + proto.MESSAGE, number=1, message=participant.Message, + ) + + +class ListMessagesRequest(proto.Message): + r"""The request message for + [Conversations.ListMessages][google.cloud.dialogflow.v2beta1.Conversations.ListMessages]. + + Attributes: + parent (str): + Required. The name of the conversation to list messages for. + Format: + ``projects//locations//conversations/`` + filter (str): + Optional. Filter on message fields. Currently predicates on + ``create_time`` and ``create_time_epoch_microseconds`` are + supported. ``create_time`` only support milliseconds + accuracy. E.g., + ``create_time_epoch_microseconds > 1551790877964485`` or + ``create_time > 2017-01-15T01:30:15.01Z``. + + For more information about filtering, see `API + Filtering `__. + page_size (int): + Optional. The maximum number of items to + return in a single page. By default 100 and at + most 1000. + page_token (str): + Optional. The next_page_token value returned from a previous + list request. + """ + + parent = proto.Field(proto.STRING, number=1) + + filter = proto.Field(proto.STRING, number=4) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class ListMessagesResponse(proto.Message): + r"""The response message for + [Conversations.ListMessages][google.cloud.dialogflow.v2beta1.Conversations.ListMessages]. + + Attributes: + messages (Sequence[google.cloud.dialogflow_v2beta1.types.Message]): + Required. The list of messages. There will be a maximum + number of items returned based on the page_size field in the + request. ``messages`` is sorted by ``create_time`` in + descending order. + next_page_token (str): + Optional. 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 + + messages = proto.RepeatedField( + proto.MESSAGE, number=1, message=participant.Message, + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2beta1/types/conversation_event.py b/google/cloud/dialogflow_v2beta1/types/conversation_event.py new file mode 100644 index 000000000..a9091e3af --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/types/conversation_event.py @@ -0,0 +1,68 @@ +# -*- 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.dialogflow_v2beta1.types import participant +from google.rpc import status_pb2 as status # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.v2beta1", manifest={"ConversationEvent",}, +) + + +class ConversationEvent(proto.Message): + r"""Represents a notification sent to Pub/Sub subscribers for + conversation lifecycle events. + + Attributes: + conversation (str): + Required. The unique identifier of the conversation this + notification refers to. Format: + ``projects//conversations/``. + type_ (google.cloud.dialogflow_v2beta1.types.ConversationEvent.Type): + Required. The type of the event that this + notification refers to. + error_status (google.rpc.status_pb2.Status): + Optional. More detailed information about an error. Only set + for type UNRECOVERABLE_ERROR_IN_PHONE_CALL. + new_message_payload (google.cloud.dialogflow_v2beta1.types.Message): + Payload of NEW_MESSAGE event. + """ + + class Type(proto.Enum): + r"""Enumeration of the types of events available.""" + TYPE_UNSPECIFIED = 0 + CONVERSATION_STARTED = 1 + CONVERSATION_FINISHED = 2 + NEW_MESSAGE = 5 + UNRECOVERABLE_ERROR = 4 + + conversation = proto.Field(proto.STRING, number=1) + + type_ = proto.Field(proto.ENUM, number=2, enum=Type,) + + error_status = proto.Field(proto.MESSAGE, number=3, message=status.Status,) + + new_message_payload = proto.Field( + proto.MESSAGE, number=4, oneof="payload", message=participant.Message, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2beta1/types/conversation_profile.py b/google/cloud/dialogflow_v2beta1/types/conversation_profile.py new file mode 100644 index 000000000..4d1c17775 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/types/conversation_profile.py @@ -0,0 +1,709 @@ +# -*- 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.dialogflow_v2beta1.types import audio_config +from google.cloud.dialogflow_v2beta1.types import participant +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.v2beta1", + manifest={ + "ConversationProfile", + "AutomatedAgentConfig", + "HumanAgentAssistantConfig", + "HumanAgentHandoffConfig", + "NotificationConfig", + "LoggingConfig", + "ListConversationProfilesRequest", + "ListConversationProfilesResponse", + "GetConversationProfileRequest", + "CreateConversationProfileRequest", + "UpdateConversationProfileRequest", + "DeleteConversationProfileRequest", + }, +) + + +class ConversationProfile(proto.Message): + r"""Defines the services to connect to incoming Dialogflow + conversations. + + Attributes: + name (str): + The unique identifier of this conversation profile. Format: + ``projects//locations//conversationProfiles/``. + display_name (str): + Required. Human readable name for this + profile. Max length 1024 bytes. + create_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Create time of the conversation + profile. + update_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. Update time of the conversation + profile. + automated_agent_config (google.cloud.dialogflow_v2beta1.types.AutomatedAgentConfig): + Configuration for an automated agent to use + with this profile. + human_agent_assistant_config (google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig): + Configuration for agent assistance to use + with this profile. + human_agent_handoff_config (google.cloud.dialogflow_v2beta1.types.HumanAgentHandoffConfig): + Configuration for connecting to a live agent. + notification_config (google.cloud.dialogflow_v2beta1.types.NotificationConfig): + Configuration for publishing conversation + lifecycle events. + logging_config (google.cloud.dialogflow_v2beta1.types.LoggingConfig): + Configuration for logging conversation + lifecycle events. + new_message_event_notification_config (google.cloud.dialogflow_v2beta1.types.NotificationConfig): + Configuration for publishing new message events. Event will + be sent in format of + [ConversationEvent][google.cloud.dialogflow.v2beta1.ConversationEvent] + stt_config (google.cloud.dialogflow_v2beta1.types.SpeechToTextConfig): + Settings for speech transcription. + language_code (str): + Language code for the conversation profile. + If not specified, the language is en-US. + Language at ConversationProfile should be set + for all non en-us languages. + """ + + name = proto.Field(proto.STRING, number=1) + + display_name = proto.Field(proto.STRING, number=2) + + create_time = proto.Field(proto.MESSAGE, number=11, message=timestamp.Timestamp,) + + update_time = proto.Field(proto.MESSAGE, number=12, message=timestamp.Timestamp,) + + automated_agent_config = proto.Field( + proto.MESSAGE, number=3, message="AutomatedAgentConfig", + ) + + human_agent_assistant_config = proto.Field( + proto.MESSAGE, number=4, message="HumanAgentAssistantConfig", + ) + + human_agent_handoff_config = proto.Field( + proto.MESSAGE, number=5, message="HumanAgentHandoffConfig", + ) + + notification_config = proto.Field( + proto.MESSAGE, number=6, message="NotificationConfig", + ) + + logging_config = proto.Field(proto.MESSAGE, number=7, message="LoggingConfig",) + + new_message_event_notification_config = proto.Field( + proto.MESSAGE, number=8, message="NotificationConfig", + ) + + stt_config = proto.Field( + proto.MESSAGE, number=9, message=audio_config.SpeechToTextConfig, + ) + + language_code = proto.Field(proto.STRING, number=10) + + +class AutomatedAgentConfig(proto.Message): + r"""Defines the Automated Agent to connect to a conversation. + + Attributes: + agent (str): + Required. ID of the Dialogflow agent environment to use. + + This project needs to either be the same project as the + conversation or you need to grant + ``service-@gcp-sa-dialogflow.iam.gserviceaccount.com`` + the ``Dialogflow API Service Agent`` role in this project. + + - For ES agents, use format: + ``projects//locations//agent/environments/``. + If environment is not specified, the default ``draft`` + environment is used. Refer to + `DetectIntentRequest `__ + for more details. + + - For CX agents, use format + ``projects//locations//agents//environments/``. + If environment is not specified, the default ``draft`` + environment is used. + """ + + agent = proto.Field(proto.STRING, number=1) + + +class HumanAgentAssistantConfig(proto.Message): + r"""Defines the Human Agent Assistant to connect to a + conversation. + + Attributes: + notification_config (google.cloud.dialogflow_v2beta1.types.NotificationConfig): + Pub/Sub topic on which to publish new agent + assistant events. + human_agent_suggestion_config (google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig.SuggestionConfig): + Configuration for agent assistance of human + agent participant. + end_user_suggestion_config (google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig.SuggestionConfig): + Configuration for agent assistance of end + user participant. + message_analysis_config (google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig.MessageAnalysisConfig): + Configuration for message analysis. + """ + + class SuggestionTriggerSettings(proto.Message): + r"""Settings of suggestion trigger. + + Attributes: + no_small_talk (bool): + Do not trigger if last utterance is small + talk. + only_end_user (bool): + Only trigger suggestion if participant role of last + utterance is END_USER. + """ + + no_small_talk = proto.Field(proto.BOOL, number=1) + + only_end_user = proto.Field(proto.BOOL, number=2) + + class SuggestionFeatureConfig(proto.Message): + r"""Config for suggestion features. + + Attributes: + suggestion_feature (google.cloud.dialogflow_v2beta1.types.SuggestionFeature): + The suggestion feature. + enable_event_based_suggestion (bool): + Automatically iterates all participants and tries to compile + suggestions. + + Supported features: ARTICLE_SUGGESTION, FAQ, + DIALOGFLOW_ASSIST. + suggestion_trigger_settings (google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig.SuggestionTriggerSettings): + Settings of suggestion trigger. + + Currently, only ARTICLE_SUGGESTION, FAQ, and + DIALOGFLOW_ASSIST will use this field. + query_config (google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig.SuggestionQueryConfig): + Configs of query. + conversation_model_config (google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig.ConversationModelConfig): + Configs of custom conversation model. + """ + + suggestion_feature = proto.Field( + proto.MESSAGE, number=5, message=participant.SuggestionFeature, + ) + + enable_event_based_suggestion = proto.Field(proto.BOOL, number=3) + + suggestion_trigger_settings = proto.Field( + proto.MESSAGE, + number=10, + message="HumanAgentAssistantConfig.SuggestionTriggerSettings", + ) + + query_config = proto.Field( + proto.MESSAGE, + number=6, + message="HumanAgentAssistantConfig.SuggestionQueryConfig", + ) + + conversation_model_config = proto.Field( + proto.MESSAGE, + number=7, + message="HumanAgentAssistantConfig.ConversationModelConfig", + ) + + class SuggestionConfig(proto.Message): + r"""Detail human agent assistant config. + + Attributes: + feature_configs (Sequence[google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig.SuggestionFeatureConfig]): + Configuration of different suggestion + features. One feature can have only one config. + group_suggestion_responses (bool): + If ``group_suggestion_responses`` is false, and there are + multiple ``feature_configs`` in ``event based suggestion`` + or StreamingAnalyzeContent, we will try to deliver + suggestions to customers as soon as we get new suggestion. + Different type of suggestions based on the same context will + be in separate Pub/Sub event or + ``StreamingAnalyzeContentResponse``. + + If ``group_suggestion_responses`` set to true. All the + suggestions to the same participant based on the same + context will be grouped into a single Pub/Sub event or + StreamingAnalyzeContentResponse. + """ + + feature_configs = proto.RepeatedField( + proto.MESSAGE, + number=2, + message="HumanAgentAssistantConfig.SuggestionFeatureConfig", + ) + + group_suggestion_responses = proto.Field(proto.BOOL, number=3) + + class SuggestionQueryConfig(proto.Message): + r"""Config for suggestion query. + + Attributes: + knowledge_base_query_source (google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig.SuggestionQueryConfig.KnowledgeBaseQuerySource): + Query from knowledgebase. It is used by: ARTICLE_SUGGESTION, + FAQ. + document_query_source (google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig.SuggestionQueryConfig.DocumentQuerySource): + Query from knowledge base document. It is used by: + SMART_REPLY, SMART_COMPOSE. + dialogflow_query_source (google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig.SuggestionQueryConfig.DialogflowQuerySource): + Query from Dialogflow agent. It is used by + DIALOGFLOW_ASSIST. + max_results (int): + Maximum number of results to return. + Currently, if unset, defaults to 10. And the max + number is 20. + confidence_threshold (float): + Confidence threshold of query result. + + Agent Assist gives each suggestion a score in the range + [0.0, 1.0], based on the relevance between the suggestion + and the current conversation context. A score of 0.0 has no + relevance, while a score of 1.0 has high relevance. Only + suggestions with a score greater than or equal to the value + of this field are included in the results. + + For a baseline model (the default), the recommended value is + in the range [0.05, 0.1]. + + For a custom model, there is no recommended value. Tune this + value by starting from a very low value and slowly + increasing until you have desired results. + + If this field is not set, it is default to 0.0, which means + that all suggestions are returned. + + Supported features: ARTICLE_SUGGESTION, FAQ, SMART_REPLY, + SMART_COMPOSE. + context_filter_settings (google.cloud.dialogflow_v2beta1.types.HumanAgentAssistantConfig.SuggestionQueryConfig.ContextFilterSettings): + Determines how recent conversation context is + filtered when generating suggestions. If + unspecified, no messages will be dropped. + """ + + class KnowledgeBaseQuerySource(proto.Message): + r"""Knowledge base source settings. + + Supported features: ARTICLE_SUGGESTION, FAQ. + + Attributes: + knowledge_bases (Sequence[str]): + Required. Knowledge bases to query. Format: + ``projects//locations//knowledgeBases/``. + Currently, only one knowledge base is supported. + """ + + knowledge_bases = proto.RepeatedField(proto.STRING, number=1) + + class DocumentQuerySource(proto.Message): + r"""Document source settings. + + Supported features: SMART_REPLY, SMART_COMPOSE. + + Attributes: + documents (Sequence[str]): + Required. Knowledge documents to query from. Format: + ``projects//locations//knowledgeBases//documents/``. + Currently, only one document is supported. + """ + + documents = proto.RepeatedField(proto.STRING, number=1) + + class DialogflowQuerySource(proto.Message): + r"""Dialogflow source setting. + + Supported feature: DIALOGFLOW_ASSIST. + + Attributes: + agent (str): + Required. The name of a dialogflow virtual agent used for + end user side intent detection and suggestion. Format: + ``projects//locations//agent``. + When multiple agents are allowed in the same Dialogflow + project. + """ + + agent = proto.Field(proto.STRING, number=1) + + class ContextFilterSettings(proto.Message): + r"""Settings that determine how to filter recent conversation + context when generating suggestions. + + Attributes: + drop_handoff_messages (bool): + If set to true, the last message from virtual + agent (hand off message) and the message before + it (trigger message of hand off) are dropped. + drop_virtual_agent_messages (bool): + If set to true, all messages from virtual + agent are dropped. + drop_ivr_messages (bool): + If set to true, all messages from ivr stage + are dropped. + """ + + drop_handoff_messages = proto.Field(proto.BOOL, number=1) + + drop_virtual_agent_messages = proto.Field(proto.BOOL, number=2) + + drop_ivr_messages = proto.Field(proto.BOOL, number=3) + + knowledge_base_query_source = proto.Field( + proto.MESSAGE, + number=1, + oneof="query_source", + message="HumanAgentAssistantConfig.SuggestionQueryConfig.KnowledgeBaseQuerySource", + ) + + document_query_source = proto.Field( + proto.MESSAGE, + number=2, + oneof="query_source", + message="HumanAgentAssistantConfig.SuggestionQueryConfig.DocumentQuerySource", + ) + + dialogflow_query_source = proto.Field( + proto.MESSAGE, + number=3, + oneof="query_source", + message="HumanAgentAssistantConfig.SuggestionQueryConfig.DialogflowQuerySource", + ) + + max_results = proto.Field(proto.INT32, number=4) + + confidence_threshold = proto.Field(proto.FLOAT, number=5) + + context_filter_settings = proto.Field( + proto.MESSAGE, + number=7, + message="HumanAgentAssistantConfig.SuggestionQueryConfig.ContextFilterSettings", + ) + + class ConversationModelConfig(proto.Message): + r"""Custom conversation models used in agent assist feature. + + Supported feature: ARTICLE_SUGGESTION, SMART_COMPOSE, SMART_REPLY. + + Attributes: + model (str): + Required. Conversation model resource name. Format: + ``projects//conversationModels/``. + """ + + model = proto.Field(proto.STRING, number=1) + + class MessageAnalysisConfig(proto.Message): + r"""Configuration for analyses to run on each conversation + message. + + Attributes: + enable_entity_extraction (bool): + Enable entity extraction in conversation messages on `agent + assist + stage `__. + If unspecified, defaults to false. + enable_sentiment_analysis (bool): + Enable sentiment analysis in conversation messages on `agent + assist + stage `__. + If unspecified, defaults to false. Sentiment analysis + inspects user input and identifies the prevailing subjective + opinion, especially to determine a user's attitude as + positive, negative, or neutral: + https://cloud.google.com/natural-language/docs/basics#sentiment_analysis + For + [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.StreamingAnalyzeContent] + method, result will be in + [StreamingAnalyzeContentResponse.message.SentimentAnalysisResult][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentResponse.message]. + For + [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent] + method, result will be in + [AnalyzeContentResponse.message.SentimentAnalysisResult][google.cloud.dialogflow.v2beta1.AnalyzeContentResponse.message] + For + [Conversations.ListMessages][google.cloud.dialogflow.v2beta1.Conversations.ListMessages] + method, result will be in + [ListMessagesResponse.messages.SentimentAnalysisResult][google.cloud.dialogflow.v2beta1.ListMessagesResponse.messages] + If Pub/Sub notification is configured, result will be in + [ConversationEvent.new_message_payload.SentimentAnalysisResult][google.cloud.dialogflow.v2beta1.ConversationEvent.new_message_payload]. + """ + + enable_entity_extraction = proto.Field(proto.BOOL, number=2) + + enable_sentiment_analysis = proto.Field(proto.BOOL, number=3) + + notification_config = proto.Field( + proto.MESSAGE, number=2, message="NotificationConfig", + ) + + human_agent_suggestion_config = proto.Field( + proto.MESSAGE, number=3, message=SuggestionConfig, + ) + + end_user_suggestion_config = proto.Field( + proto.MESSAGE, number=4, message=SuggestionConfig, + ) + + message_analysis_config = proto.Field( + proto.MESSAGE, number=5, message=MessageAnalysisConfig, + ) + + +class HumanAgentHandoffConfig(proto.Message): + r"""Defines the hand off to a live agent, typically on which + external agent service provider to connect to a conversation. + + Attributes: + live_person_config (google.cloud.dialogflow_v2beta1.types.HumanAgentHandoffConfig.LivePersonConfig): + Uses LivePerson (https://www.liveperson.com). + salesforce_live_agent_config (google.cloud.dialogflow_v2beta1.types.HumanAgentHandoffConfig.SalesforceLiveAgentConfig): + Uses Salesforce Live Agent. + """ + + class LivePersonConfig(proto.Message): + r"""Configuration specific to LivePerson + (https://www.liveperson.com). + + Attributes: + account_number (str): + Required. Account number of the LivePerson + account to connect. This is the account number + you input at the login page. + """ + + account_number = proto.Field(proto.STRING, number=1) + + class SalesforceLiveAgentConfig(proto.Message): + r"""Configuration specific to Salesforce Live Agent. + + Attributes: + organization_id (str): + Required. The organization ID of the + Salesforce account. + deployment_id (str): + Required. Live Agent deployment ID. + button_id (str): + Required. Live Agent chat button ID. + endpoint_domain (str): + Required. Domain of the Live Agent endpoint for this agent. + You can find the endpoint URL in the ``Live Agent settings`` + page. For example if URL has the form + https://d.la4-c2-phx.salesforceliveagent.com/..., you should + fill in d.la4-c2-phx.salesforceliveagent.com. + """ + + organization_id = proto.Field(proto.STRING, number=1) + + deployment_id = proto.Field(proto.STRING, number=2) + + button_id = proto.Field(proto.STRING, number=3) + + endpoint_domain = proto.Field(proto.STRING, number=4) + + live_person_config = proto.Field( + proto.MESSAGE, number=1, oneof="agent_service", message=LivePersonConfig, + ) + + salesforce_live_agent_config = proto.Field( + proto.MESSAGE, + number=2, + oneof="agent_service", + message=SalesforceLiveAgentConfig, + ) + + +class NotificationConfig(proto.Message): + r"""Defines notification behavior. + + Attributes: + topic (str): + Name of the Pub/Sub topic to publish conversation events + like + [CONVERSATION_STARTED][google.cloud.dialogflow.v2beta1.ConversationEvent.Type.CONVERSATION_STARTED] + as serialized + [ConversationEvent][google.cloud.dialogflow.v2beta1.ConversationEvent] + protos. + + Notification works for phone calls, if this topic either is + in the same project as the conversation or you grant + ``service-@gcp-sa-dialogflow.iam.gserviceaccount.com`` + the ``Dialogflow Service Agent`` role in the topic project. + + Format: + ``projects//locations//topics/``. + message_format (google.cloud.dialogflow_v2beta1.types.NotificationConfig.MessageFormat): + Format of message. + """ + + class MessageFormat(proto.Enum): + r"""Format of cloud pub/sub message.""" + MESSAGE_FORMAT_UNSPECIFIED = 0 + PROTO = 1 + JSON = 2 + + topic = proto.Field(proto.STRING, number=1) + + message_format = proto.Field(proto.ENUM, number=2, enum=MessageFormat,) + + +class LoggingConfig(proto.Message): + r"""Defines logging behavior for conversation lifecycle events. + + Attributes: + enable_stackdriver_logging (bool): + Whether to log conversation events like + [CONVERSATION_STARTED][google.cloud.dialogflow.v2beta1.ConversationEvent.Type.CONVERSATION_STARTED] + to Stackdriver in the conversation project as JSON format + [ConversationEvent][google.cloud.dialogflow.v2beta1.ConversationEvent] + protos. + """ + + enable_stackdriver_logging = proto.Field(proto.BOOL, number=3) + + +class ListConversationProfilesRequest(proto.Message): + r"""The request message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfiles.ListConversationProfiles]. + + Attributes: + parent (str): + Required. The project to list all conversation profiles + from. 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 ListConversationProfilesResponse(proto.Message): + r"""The response message for + [ConversationProfiles.ListConversationProfiles][google.cloud.dialogflow.v2beta1.ConversationProfiles.ListConversationProfiles]. + + Attributes: + conversation_profiles (Sequence[google.cloud.dialogflow_v2beta1.types.ConversationProfile]): + The list of project conversation profiles. There is 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 + + conversation_profiles = proto.RepeatedField( + proto.MESSAGE, number=1, message="ConversationProfile", + ) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class GetConversationProfileRequest(proto.Message): + r"""The request message for + [ConversationProfiles.GetConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.GetConversationProfile]. + + Attributes: + name (str): + Required. The resource name of the conversation profile. + Format: + ``projects//locations//conversationProfiles/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class CreateConversationProfileRequest(proto.Message): + r"""The request message for + [ConversationProfiles.CreateConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.CreateConversationProfile]. + + Attributes: + parent (str): + Required. The project to create a conversation profile for. + Format: ``projects//locations/``. + conversation_profile (google.cloud.dialogflow_v2beta1.types.ConversationProfile): + Required. The conversation profile to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + conversation_profile = proto.Field( + proto.MESSAGE, number=2, message="ConversationProfile", + ) + + +class UpdateConversationProfileRequest(proto.Message): + r"""The request message for + [ConversationProfiles.UpdateConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.UpdateConversationProfile]. + + Attributes: + conversation_profile (google.cloud.dialogflow_v2beta1.types.ConversationProfile): + Required. The conversation profile to update. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. The mask to control which fields to + update. + """ + + conversation_profile = proto.Field( + proto.MESSAGE, number=1, message="ConversationProfile", + ) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class DeleteConversationProfileRequest(proto.Message): + r"""The request message for + [ConversationProfiles.DeleteConversationProfile][google.cloud.dialogflow.v2beta1.ConversationProfiles.DeleteConversationProfile]. + + This operation fails if the conversation profile is still referenced + from a phone number. + + Attributes: + name (str): + Required. The name of the conversation profile to delete. + Format: + ``projects//locations//conversationProfiles/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2beta1/types/document.py b/google/cloud/dialogflow_v2beta1/types/document.py index 6cba8305b..3a90e221e 100644 --- a/google/cloud/dialogflow_v2beta1/types/document.py +++ b/google/cloud/dialogflow_v2beta1/types/document.py @@ -32,6 +32,9 @@ "ListDocumentsRequest", "ListDocumentsResponse", "CreateDocumentRequest", + "ImportDocumentsRequest", + "ImportDocumentTemplate", + "ImportDocumentsResponse", "DeleteDocumentRequest", "UpdateDocumentRequest", "KnowledgeOperationMetadata", @@ -61,7 +64,7 @@ class Document(proto.Message): the creation request fails. mime_type (str): Required. The MIME type of this document. - knowledge_types (Sequence[~.gcd_document.Document.KnowledgeType]): + knowledge_types (Sequence[google.cloud.dialogflow_v2beta1.types.Document.KnowledgeType]): Required. The knowledge type of document content. content_uri (str): @@ -102,11 +105,17 @@ class Document(proto.Message): system will not try to reload the document anymore. You need to manually reload the document successfully by calling ``ReloadDocument`` and clear the errors. - latest_reload_status (~.gcd_document.Document.ReloadStatus): + latest_reload_status (google.cloud.dialogflow_v2beta1.types.Document.ReloadStatus): Output only. The time and status of the latest reload. This reload may have been triggered automatically or manually and may not have succeeded. + metadata (Sequence[google.cloud.dialogflow_v2beta1.types.Document.MetadataEntry]): + Optional. Metadata for the document. The metadata supports + arbitrary key-value pairs. Suggested use cases include + storing a document's title, an external URL distinct from + the document's content_uri, etc. The max size of a ``key`` + or a ``value`` of the metadata is 1024 bytes. """ class KnowledgeType(proto.Enum): @@ -114,17 +123,19 @@ class KnowledgeType(proto.Enum): KNOWLEDGE_TYPE_UNSPECIFIED = 0 FAQ = 1 EXTRACTIVE_QA = 2 + ARTICLE_SUGGESTION = 3 + SMART_REPLY = 4 class ReloadStatus(proto.Message): r"""The status of a reload attempt. Attributes: - time (~.timestamp.Timestamp): + time (google.protobuf.timestamp_pb2.Timestamp): Output only. The time of a reload attempt. This reload may have been triggered automatically or manually and may not have succeeded. - status (~.gr_status.Status): + status (google.rpc.status_pb2.Status): Output only. The status of a reload attempt or the initial load. """ @@ -151,6 +162,8 @@ class ReloadStatus(proto.Message): latest_reload_status = proto.Field(proto.MESSAGE, number=12, message=ReloadStatus,) + metadata = proto.MapField(proto.STRING, proto.STRING, number=7) + class GetDocumentRequest(proto.Message): r"""Request message for @@ -220,7 +233,7 @@ class ListDocumentsResponse(proto.Message): [Documents.ListDocuments][google.cloud.dialogflow.v2beta1.Documents.ListDocuments]. Attributes: - documents (Sequence[~.gcd_document.Document]): + documents (Sequence[google.cloud.dialogflow_v2beta1.types.Document]): The list of documents. next_page_token (str): Token to retrieve the next page of results, @@ -246,7 +259,7 @@ class CreateDocumentRequest(proto.Message): Required. The knowledge base to create a document for. Format: ``projects//locations//knowledgeBases/``. - document (~.gcd_document.Document): + document (google.cloud.dialogflow_v2beta1.types.Document): Required. The document to create. import_gcs_custom_metadata (bool): Whether to import custom metadata from Google @@ -261,6 +274,83 @@ class CreateDocumentRequest(proto.Message): import_gcs_custom_metadata = proto.Field(proto.BOOL, number=3) +class ImportDocumentsRequest(proto.Message): + r"""Request message for + [Documents.ImportDocuments][google.cloud.dialogflow.v2beta1.Documents.ImportDocuments]. + + Attributes: + parent (str): + Required. The knowledge base to import documents into. + Format: + ``projects//locations//knowledgeBases/``. + gcs_source (google.cloud.dialogflow_v2beta1.types.GcsSources): + The Google Cloud Storage location for the documents. The + path can include a wildcard. + + These URIs may have the forms + ``gs:///``. + ``gs:////*.``. + document_template (google.cloud.dialogflow_v2beta1.types.ImportDocumentTemplate): + Required. Document template used for + importing all the documents. + import_gcs_custom_metadata (bool): + Whether to import custom metadata from Google + Cloud Storage. Only valid when the document + source is Google Cloud Storage URI. + """ + + parent = proto.Field(proto.STRING, number=1) + + gcs_source = proto.Field( + proto.MESSAGE, number=2, oneof="source", message=gcs.GcsSources, + ) + + document_template = proto.Field( + proto.MESSAGE, number=3, message="ImportDocumentTemplate", + ) + + import_gcs_custom_metadata = proto.Field(proto.BOOL, number=4) + + +class ImportDocumentTemplate(proto.Message): + r"""The template used for importing documents. + + Attributes: + mime_type (str): + Required. The MIME type of the document. + knowledge_types (Sequence[google.cloud.dialogflow_v2beta1.types.Document.KnowledgeType]): + Required. The knowledge type of document + content. + metadata (Sequence[google.cloud.dialogflow_v2beta1.types.ImportDocumentTemplate.MetadataEntry]): + Metadata for the document. The metadata supports arbitrary + key-value pairs. Suggested use cases include storing a + document's title, an external URL distinct from the + document's content_uri, etc. The max size of a ``key`` or a + ``value`` of the metadata is 1024 bytes. + """ + + mime_type = proto.Field(proto.STRING, number=1) + + knowledge_types = proto.RepeatedField( + proto.ENUM, number=2, enum="Document.KnowledgeType", + ) + + metadata = proto.MapField(proto.STRING, proto.STRING, number=3) + + +class ImportDocumentsResponse(proto.Message): + r"""Response message for + [Documents.ImportDocuments][google.cloud.dialogflow.v2beta1.Documents.ImportDocuments]. + + Attributes: + warnings (Sequence[google.rpc.status_pb2.Status]): + Includes details about skipped documents or + any other warnings. + """ + + warnings = proto.RepeatedField(proto.MESSAGE, number=1, message=gr_status.Status,) + + class DeleteDocumentRequest(proto.Message): r"""Request message for [Documents.DeleteDocument][google.cloud.dialogflow.v2beta1.Documents.DeleteDocument]. @@ -279,9 +369,9 @@ class UpdateDocumentRequest(proto.Message): [Documents.UpdateDocument][google.cloud.dialogflow.v2beta1.Documents.UpdateDocument]. Attributes: - document (~.gcd_document.Document): + document (google.cloud.dialogflow_v2beta1.types.Document): Required. The document to update. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. Not specified means ``update all``. Currently, only ``display_name`` can be updated, an InvalidArgument will be returned for attempting to update other fields. @@ -297,7 +387,7 @@ class KnowledgeOperationMetadata(proto.Message): operations. Attributes: - state (~.gcd_document.KnowledgeOperationMetadata.State): + state (google.cloud.dialogflow_v2beta1.types.KnowledgeOperationMetadata.State): Required. Output only. The current state of this operation. """ @@ -320,7 +410,7 @@ class ReloadDocumentRequest(proto.Message): name (str): Required. The name of the document to reload. Format: ``projects//locations//knowledgeBases//documents/`` - gcs_source (~.gcs.GcsSource): + gcs_source (google.cloud.dialogflow_v2beta1.types.GcsSource): The path for a Cloud Storage source file for reloading document content. If not provided, the Document's existing source will be reloaded. diff --git a/google/cloud/dialogflow_v2beta1/types/entity_type.py b/google/cloud/dialogflow_v2beta1/types/entity_type.py index b26f8ecfd..e395607b1 100644 --- a/google/cloud/dialogflow_v2beta1/types/entity_type.py +++ b/google/cloud/dialogflow_v2beta1/types/entity_type.py @@ -69,12 +69,12 @@ class EntityType(proto.Message): - ``projects//locations//agent/entityTypes/`` display_name (str): Required. The name of the entity type. - kind (~.gcd_entity_type.EntityType.Kind): + kind (google.cloud.dialogflow_v2beta1.types.EntityType.Kind): Required. Indicates the kind of entity type. - auto_expansion_mode (~.gcd_entity_type.EntityType.AutoExpansionMode): + auto_expansion_mode (google.cloud.dialogflow_v2beta1.types.EntityType.AutoExpansionMode): Optional. Indicates whether the entity type can be automatically expanded. - entities (Sequence[~.gcd_entity_type.EntityType.Entity]): + entities (Sequence[google.cloud.dialogflow_v2beta1.types.EntityType.Entity]): Optional. The collection of entity entries associated with the entity type. enable_fuzzy_extraction (bool): @@ -183,7 +183,7 @@ class ListEntityTypesResponse(proto.Message): [EntityTypes.ListEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.ListEntityTypes]. Attributes: - entity_types (Sequence[~.gcd_entity_type.EntityType]): + entity_types (Sequence[google.cloud.dialogflow_v2beta1.types.EntityType]): The list of agent entity types. There will be a maximum number of items returned based on the page_size field in the request. @@ -236,7 +236,7 @@ class CreateEntityTypeRequest(proto.Message): - ``projects//agent`` - ``projects//locations//agent`` - entity_type (~.gcd_entity_type.EntityType): + entity_type (google.cloud.dialogflow_v2beta1.types.EntityType): Required. The entity type to create. language_code (str): Optional. The language used to access language-specific @@ -258,7 +258,7 @@ class UpdateEntityTypeRequest(proto.Message): [EntityTypes.UpdateEntityType][google.cloud.dialogflow.v2beta1.EntityTypes.UpdateEntityType]. Attributes: - entity_type (~.gcd_entity_type.EntityType): + entity_type (google.cloud.dialogflow_v2beta1.types.EntityType): Required. The entity type to update. language_code (str): Optional. The language used to access language-specific @@ -266,7 +266,7 @@ class UpdateEntityTypeRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ @@ -311,7 +311,7 @@ class BatchUpdateEntityTypesRequest(proto.Message): file format can either be a serialized proto (of EntityBatch type) or a JSON object. Note: The URI must start with "gs://". - entity_type_batch_inline (~.gcd_entity_type.EntityTypeBatch): + entity_type_batch_inline (google.cloud.dialogflow_v2beta1.types.EntityTypeBatch): The collection of entity types to update or create. language_code (str): @@ -320,7 +320,7 @@ class BatchUpdateEntityTypesRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ @@ -345,7 +345,7 @@ class BatchUpdateEntityTypesResponse(proto.Message): [EntityTypes.BatchUpdateEntityTypes][google.cloud.dialogflow.v2beta1.EntityTypes.BatchUpdateEntityTypes]. Attributes: - entity_types (Sequence[~.gcd_entity_type.EntityType]): + entity_types (Sequence[google.cloud.dialogflow_v2beta1.types.EntityType]): The collection of updated or created entity types. """ @@ -385,7 +385,7 @@ class BatchCreateEntitiesRequest(proto.Message): - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` - entities (Sequence[~.gcd_entity_type.EntityType.Entity]): + entities (Sequence[google.cloud.dialogflow_v2beta1.types.EntityType.Entity]): Required. The entities to create. language_code (str): Optional. The language used to access language-specific @@ -415,7 +415,7 @@ class BatchUpdateEntitiesRequest(proto.Message): - ``projects//agent/entityTypes/`` - ``projects//locations//agent/entityTypes/`` - entities (Sequence[~.gcd_entity_type.EntityType.Entity]): + entities (Sequence[google.cloud.dialogflow_v2beta1.types.EntityType.Entity]): Required. The entities to update or create. language_code (str): Optional. The language used to access language-specific @@ -423,7 +423,7 @@ class BatchUpdateEntitiesRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ @@ -474,7 +474,7 @@ class EntityTypeBatch(proto.Message): types. Attributes: - entity_types (Sequence[~.gcd_entity_type.EntityType]): + entity_types (Sequence[google.cloud.dialogflow_v2beta1.types.EntityType]): A collection of entity types. """ diff --git a/google/cloud/dialogflow_v2beta1/types/environment.py b/google/cloud/dialogflow_v2beta1/types/environment.py index 137f9e3bf..9f5c887de 100644 --- a/google/cloud/dialogflow_v2beta1/types/environment.py +++ b/google/cloud/dialogflow_v2beta1/types/environment.py @@ -66,11 +66,11 @@ class Environment(proto.Message): - ``projects//agent/versions/`` - ``projects//locations//agent/versions/`` - state (~.environment.Environment.State): + state (google.cloud.dialogflow_v2beta1.types.Environment.State): Output only. The state of this environment. This field is read-only, i.e., it cannot be set by create and update methods. - update_time (~.timestamp.Timestamp): + update_time (google.protobuf.timestamp_pb2.Timestamp): Output only. The last update time of this environment. This field is read-only, i.e., it cannot be set by create and update methods. @@ -131,7 +131,7 @@ class ListEnvironmentsResponse(proto.Message): [Environments.ListEnvironments][google.cloud.dialogflow.v2beta1.Environments.ListEnvironments]. Attributes: - environments (Sequence[~.environment.Environment]): + environments (Sequence[google.cloud.dialogflow_v2beta1.types.Environment]): The list of agent environments. There will be a maximum number of items returned based on the page_size field in the request. diff --git a/google/cloud/dialogflow_v2beta1/types/gcs.py b/google/cloud/dialogflow_v2beta1/types/gcs.py index 258f2dde9..17dd7573a 100644 --- a/google/cloud/dialogflow_v2beta1/types/gcs.py +++ b/google/cloud/dialogflow_v2beta1/types/gcs.py @@ -19,10 +19,25 @@ __protobuf__ = proto.module( - package="google.cloud.dialogflow.v2beta1", manifest={"GcsSource",}, + package="google.cloud.dialogflow.v2beta1", manifest={"GcsSources", "GcsSource",}, ) +class GcsSources(proto.Message): + r"""Google Cloud Storage locations for the inputs. + + Attributes: + uris (Sequence[str]): + Required. Google Cloud Storage URIs for the + inputs. A URI is of the form: + gs://bucket/object-prefix-or-name + Whether a prefix or name is used depends on the + use case. + """ + + uris = proto.RepeatedField(proto.STRING, number=2) + + class GcsSource(proto.Message): r"""Google Cloud Storage location for single input. diff --git a/google/cloud/dialogflow_v2beta1/types/human_agent_assistant_event.py b/google/cloud/dialogflow_v2beta1/types/human_agent_assistant_event.py new file mode 100644 index 000000000..66d6bf2c9 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/types/human_agent_assistant_event.py @@ -0,0 +1,63 @@ +# -*- 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.dialogflow_v2beta1.types import participant as gcd_participant + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.v2beta1", manifest={"HumanAgentAssistantEvent",}, +) + + +class HumanAgentAssistantEvent(proto.Message): + r"""Output only. Represents a notification sent to Pub/Sub + subscribers for agent assistant events in a specific + conversation. + + Attributes: + conversation (str): + The conversation this notification refers to. Format: + ``projects//conversations/``. + participant (str): + The participant that the suggestion is compiled for. And + This field is used to call + [Participants.ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions] + API. Format: + ``projects//conversations//participants/``. + It will not be set in legacy workflow. + [HumanAgentAssistantConfig.name][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.name] + for more information. + suggestion_results (Sequence[google.cloud.dialogflow_v2beta1.types.SuggestionResult]): + The suggestion results payload that this notification refers + to. It will only be set when + [HumanAgentAssistantConfig.SuggestionConfig.group_suggestion_responses][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.SuggestionConfig.group_suggestion_responses] + sets to true. + """ + + conversation = proto.Field(proto.STRING, number=1) + + participant = proto.Field(proto.STRING, number=3) + + suggestion_results = proto.RepeatedField( + proto.MESSAGE, number=5, message=gcd_participant.SuggestionResult, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2beta1/types/intent.py b/google/cloud/dialogflow_v2beta1/types/intent.py index bf83633c4..75dea97f7 100644 --- a/google/cloud/dialogflow_v2beta1/types/intent.py +++ b/google/cloud/dialogflow_v2beta1/types/intent.py @@ -76,7 +76,7 @@ class Intent(proto.Message): - ``projects//locations//agent/intents/`` display_name (str): Required. The name of this intent. - webhook_state (~.gcd_intent.Intent.WebhookState): + webhook_state (google.cloud.dialogflow_v2beta1.types.Intent.WebhookState): Optional. Indicates whether webhooks are enabled for the intent. priority (int): @@ -111,6 +111,11 @@ class Intent(proto.Message): then this intent is not taken into account during inference in ``ML ONLY`` match mode. Also, auto-markup in the UI is turned off. + live_agent_handoff (bool): + Optional. Indicates that a live agent should be brought in + to handle the interaction with the user. In most cases, when + you set this flag to true, you would also want to set + end_interaction to true as well. Default is false. end_interaction (bool): Optional. Indicates that this intent ends an interaction. Some integrations (e.g., Actions on @@ -130,14 +135,14 @@ class Intent(proto.Message): be present in the active user session for an event to trigger this intent. Event names are limited to 150 characters. - training_phrases (Sequence[~.gcd_intent.Intent.TrainingPhrase]): + training_phrases (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.TrainingPhrase]): Optional. The collection of examples that the agent is trained on. action (str): Optional. The name of the action associated with the intent. Note: The action name must not contain whitespaces. - output_contexts (Sequence[~.context.Context]): + output_contexts (Sequence[google.cloud.dialogflow_v2beta1.types.Context]): Optional. The collection of contexts that are activated when the intent is matched. Context messages in this collection should not set the parameters field. Setting the @@ -148,13 +153,13 @@ class Intent(proto.Message): Optional. Indicates whether to delete all contexts in the current session when this intent is matched. - parameters (Sequence[~.gcd_intent.Intent.Parameter]): + parameters (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Parameter]): Optional. The collection of parameters associated with the intent. - messages (Sequence[~.gcd_intent.Intent.Message]): + messages (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message]): Optional. The collection of rich messages corresponding to the ``Response`` field in the Dialogflow console. - default_response_platforms (Sequence[~.gcd_intent.Intent.Message.Platform]): + default_response_platforms (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.Platform]): Optional. The list of platforms for which the first responses will be copied from the messages in PLATFORM_UNSPECIFIED (i.e. default platform). @@ -175,7 +180,7 @@ class Intent(proto.Message): It identifies the parent followup intent. Format: ``projects//agent/intents/``. - followup_intent_info (Sequence[~.gcd_intent.Intent.FollowupIntentInfo]): + followup_intent_info (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.FollowupIntentInfo]): Output only. Information about all followup intents that have this intent as a direct or indirect parent. We populate this field only in @@ -195,9 +200,9 @@ class TrainingPhrase(proto.Message): name (str): Output only. The unique identifier of this training phrase. - type_ (~.gcd_intent.Intent.TrainingPhrase.Type): + type_ (google.cloud.dialogflow_v2beta1.types.Intent.TrainingPhrase.Type): Required. The type of the training phrase. - parts (Sequence[~.gcd_intent.Intent.TrainingPhrase.Part]): + parts (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.TrainingPhrase.Part]): Required. The ordered list of training phrase parts. The parts are concatenated in order to form the training phrase. @@ -336,56 +341,56 @@ class Message(proto.Message): r"""Corresponds to the ``Response`` field in the Dialogflow console. Attributes: - text (~.gcd_intent.Intent.Message.Text): + text (google.cloud.dialogflow_v2beta1.types.Intent.Message.Text): Returns a text response. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2beta1.types.Intent.Message.Image): Displays an image. - quick_replies (~.gcd_intent.Intent.Message.QuickReplies): + quick_replies (google.cloud.dialogflow_v2beta1.types.Intent.Message.QuickReplies): Displays quick replies. - card (~.gcd_intent.Intent.Message.Card): + card (google.cloud.dialogflow_v2beta1.types.Intent.Message.Card): Displays a card. - payload (~.struct.Struct): + payload (google.protobuf.struct_pb2.Struct): A custom platform-specific response. - simple_responses (~.gcd_intent.Intent.Message.SimpleResponses): + simple_responses (google.cloud.dialogflow_v2beta1.types.Intent.Message.SimpleResponses): Returns a voice or text-only response for Actions on Google. - basic_card (~.gcd_intent.Intent.Message.BasicCard): + basic_card (google.cloud.dialogflow_v2beta1.types.Intent.Message.BasicCard): Displays a basic card for Actions on Google. - suggestions (~.gcd_intent.Intent.Message.Suggestions): + suggestions (google.cloud.dialogflow_v2beta1.types.Intent.Message.Suggestions): Displays suggestion chips for Actions on Google. - link_out_suggestion (~.gcd_intent.Intent.Message.LinkOutSuggestion): + link_out_suggestion (google.cloud.dialogflow_v2beta1.types.Intent.Message.LinkOutSuggestion): Displays a link out suggestion chip for Actions on Google. - list_select (~.gcd_intent.Intent.Message.ListSelect): + list_select (google.cloud.dialogflow_v2beta1.types.Intent.Message.ListSelect): Displays a list card for Actions on Google. - carousel_select (~.gcd_intent.Intent.Message.CarouselSelect): + carousel_select (google.cloud.dialogflow_v2beta1.types.Intent.Message.CarouselSelect): Displays a carousel card for Actions on Google. - telephony_play_audio (~.gcd_intent.Intent.Message.TelephonyPlayAudio): + telephony_play_audio (google.cloud.dialogflow_v2beta1.types.Intent.Message.TelephonyPlayAudio): Plays audio from a file in Telephony Gateway. - telephony_synthesize_speech (~.gcd_intent.Intent.Message.TelephonySynthesizeSpeech): + telephony_synthesize_speech (google.cloud.dialogflow_v2beta1.types.Intent.Message.TelephonySynthesizeSpeech): Synthesizes speech in Telephony Gateway. - telephony_transfer_call (~.gcd_intent.Intent.Message.TelephonyTransferCall): + telephony_transfer_call (google.cloud.dialogflow_v2beta1.types.Intent.Message.TelephonyTransferCall): Transfers the call in Telephony Gateway. - rbm_text (~.gcd_intent.Intent.Message.RbmText): + rbm_text (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmText): Rich Business Messaging (RBM) text response. RBM allows businesses to send enriched and branded versions of SMS. See https://jibe.google.com/business-messaging. - rbm_standalone_rich_card (~.gcd_intent.Intent.Message.RbmStandaloneCard): + rbm_standalone_rich_card (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmStandaloneCard): Standalone Rich Business Messaging (RBM) rich card response. - rbm_carousel_rich_card (~.gcd_intent.Intent.Message.RbmCarouselCard): + rbm_carousel_rich_card (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmCarouselCard): Rich Business Messaging (RBM) carousel rich card response. - browse_carousel_card (~.gcd_intent.Intent.Message.BrowseCarouselCard): + browse_carousel_card (google.cloud.dialogflow_v2beta1.types.Intent.Message.BrowseCarouselCard): Browse carousel card for Actions on Google. - table_card (~.gcd_intent.Intent.Message.TableCard): + table_card (google.cloud.dialogflow_v2beta1.types.Intent.Message.TableCard): Table card for Actions on Google. - media_content (~.gcd_intent.Intent.Message.MediaContent): + media_content (google.cloud.dialogflow_v2beta1.types.Intent.Message.MediaContent): The media content card for Actions on Google. - platform (~.gcd_intent.Intent.Message.Platform): + platform (google.cloud.dialogflow_v2beta1.types.Intent.Message.Platform): Optional. The platform that this message is intended for. """ @@ -459,7 +464,7 @@ class Card(proto.Message): image_uri (str): Optional. The public URI to an image file for the card. - buttons (Sequence[~.gcd_intent.Intent.Message.Card.Button]): + buttons (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.Card.Button]): Optional. The collection of card buttons. """ @@ -516,7 +521,7 @@ class SimpleResponses(proto.Message): ``SimpleResponse``. Attributes: - simple_responses (Sequence[~.gcd_intent.Intent.Message.SimpleResponse]): + simple_responses (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.SimpleResponse]): Required. The list of simple responses. """ @@ -535,9 +540,9 @@ class BasicCard(proto.Message): formatted_text (str): Required, unless image is present. The body text of the card. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2beta1.types.Intent.Message.Image): Optional. The image for the card. - buttons (Sequence[~.gcd_intent.Intent.Message.BasicCard.Button]): + buttons (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.BasicCard.Button]): Optional. The collection of card buttons. """ @@ -547,7 +552,7 @@ class Button(proto.Message): Attributes: title (str): Required. The title of the button. - open_uri_action (~.gcd_intent.Intent.Message.BasicCard.Button.OpenUriAction): + open_uri_action (google.cloud.dialogflow_v2beta1.types.Intent.Message.BasicCard.Button.OpenUriAction): Required. Action to take when a user taps on the button. """ @@ -600,7 +605,7 @@ class Suggestions(proto.Message): r"""The collection of suggestions. Attributes: - suggestions (Sequence[~.gcd_intent.Intent.Message.Suggestion]): + suggestions (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.Suggestion]): Required. The list of suggested replies. """ @@ -631,7 +636,7 @@ class ListSelect(proto.Message): Attributes: title (str): Optional. The overall title of the list. - items (Sequence[~.gcd_intent.Intent.Message.ListSelect.Item]): + items (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.ListSelect.Item]): Required. List items. subtitle (str): Optional. Subtitle of the list. @@ -641,14 +646,14 @@ class Item(proto.Message): r"""An item in the list. Attributes: - info (~.gcd_intent.Intent.Message.SelectItemInfo): + info (google.cloud.dialogflow_v2beta1.types.Intent.Message.SelectItemInfo): Required. Additional information about this option. title (str): Required. The title of the list item. description (str): Optional. The main text describing the item. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2beta1.types.Intent.Message.Image): Optional. The image to display. """ @@ -676,7 +681,7 @@ class CarouselSelect(proto.Message): r"""The card for presenting a carousel of options to select from. Attributes: - items (Sequence[~.gcd_intent.Intent.Message.CarouselSelect.Item]): + items (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.CarouselSelect.Item]): Required. Carousel items. """ @@ -684,14 +689,14 @@ class Item(proto.Message): r"""An item in the carousel. Attributes: - info (~.gcd_intent.Intent.Message.SelectItemInfo): + info (google.cloud.dialogflow_v2beta1.types.Intent.Message.SelectItemInfo): Required. Additional info about the option item. title (str): Required. Title of the carousel item. description (str): Optional. The body text of the card. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2beta1.types.Intent.Message.Image): Optional. The image to display. """ @@ -794,7 +799,7 @@ class RbmText(proto.Message): text (str): Required. Text sent and displayed to the user. - rbm_suggestion (Sequence[~.gcd_intent.Intent.Message.RbmSuggestion]): + rbm_suggestion (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmSuggestion]): Optional. One or more suggestions to show to the user. """ @@ -817,10 +822,10 @@ class RbmCarouselCard(proto.Message): instead. Attributes: - card_width (~.gcd_intent.Intent.Message.RbmCarouselCard.CardWidth): + card_width (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmCarouselCard.CardWidth): Required. The width of the cards in the carousel. - card_contents (Sequence[~.gcd_intent.Intent.Message.RbmCardContent]): + card_contents (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmCardContent]): Required. The cards in the carousel. A carousel must have at least 2 cards and at most 10. @@ -851,13 +856,13 @@ class RbmStandaloneCard(proto.Message): but carousel cards will give you less control over the card layout. Attributes: - card_orientation (~.gcd_intent.Intent.Message.RbmStandaloneCard.CardOrientation): + card_orientation (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmStandaloneCard.CardOrientation): Required. Orientation of the card. - thumbnail_image_alignment (~.gcd_intent.Intent.Message.RbmStandaloneCard.ThumbnailImageAlignment): + thumbnail_image_alignment (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmStandaloneCard.ThumbnailImageAlignment): Required if orientation is horizontal. Image preview alignment for standalone cards with horizontal layout. - card_content (~.gcd_intent.Intent.Message.RbmCardContent): + card_content (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmCardContent): Required. Card content. """ @@ -905,11 +910,11 @@ class RbmCardContent(proto.Message): 2000 bytes). At least one of the title, description or media must be set. - media (~.gcd_intent.Intent.Message.RbmCardContent.RbmMedia): + media (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmCardContent.RbmMedia): Optional. However at least one of the title, description or media must be set. Media (image, GIF or a video) to include in the card. - suggestions (Sequence[~.gcd_intent.Intent.Message.RbmSuggestion]): + suggestions (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmSuggestion]): Optional. List of suggestions to include in the card. """ @@ -950,7 +955,7 @@ class RbmMedia(proto.Message): file. Depending on the user's setting, the file may not download automatically and may require the user to tap a download button. - height (~.gcd_intent.Intent.Message.RbmCardContent.RbmMedia.Height): + height (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmCardContent.RbmMedia.Height): Required for cards with vertical orientation. The height of the media within a rich card with a vertical layout. For a standalone card with @@ -995,10 +1000,10 @@ class RbmSuggestion(proto.Message): action (like opening a web uri). Attributes: - reply (~.gcd_intent.Intent.Message.RbmSuggestedReply): + reply (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmSuggestedReply): Predefined replies for user to select instead of typing - action (~.gcd_intent.Intent.Message.RbmSuggestedAction): + action (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmSuggestedAction): Predefined client side actions that user can choose """ @@ -1049,13 +1054,13 @@ class RbmSuggestedAction(proto.Message): action. This data will be also forwarded to webhook to allow performing custom business logic. - dial (~.gcd_intent.Intent.Message.RbmSuggestedAction.RbmSuggestedActionDial): + dial (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmSuggestedAction.RbmSuggestedActionDial): Suggested client side action: Dial a phone number - open_url (~.gcd_intent.Intent.Message.RbmSuggestedAction.RbmSuggestedActionOpenUri): + open_url (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmSuggestedAction.RbmSuggestedActionOpenUri): Suggested client side action: Open a URI on device - share_location (~.gcd_intent.Intent.Message.RbmSuggestedAction.RbmSuggestedActionShareLocation): + share_location (google.cloud.dialogflow_v2beta1.types.Intent.Message.RbmSuggestedAction.RbmSuggestedActionShareLocation): Suggested client side action: Share user location """ @@ -1122,10 +1127,10 @@ class MediaContent(proto.Message): r"""The media content card for Actions on Google. Attributes: - media_type (~.gcd_intent.Intent.Message.MediaContent.ResponseMediaType): + media_type (google.cloud.dialogflow_v2beta1.types.Intent.Message.MediaContent.ResponseMediaType): Optional. What type of media is the content (ie "audio"). - media_objects (Sequence[~.gcd_intent.Intent.Message.MediaContent.ResponseMediaObject]): + media_objects (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.MediaContent.ResponseMediaObject]): Required. List of media objects. """ @@ -1142,10 +1147,10 @@ class ResponseMediaObject(proto.Message): Required. Name of media card. description (str): Optional. Description of media card. - large_image (~.gcd_intent.Intent.Message.Image): + large_image (google.cloud.dialogflow_v2beta1.types.Intent.Message.Image): Optional. Image to display above media content. - icon (~.gcd_intent.Intent.Message.Image): + icon (google.cloud.dialogflow_v2beta1.types.Intent.Message.Image): Optional. Icon to display above media content. content_url (str): @@ -1189,11 +1194,11 @@ class BrowseCarouselCard(proto.Message): https://developers.google.com/actions/assistant/responses#browsing_carousel Attributes: - items (Sequence[~.gcd_intent.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem]): + items (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem]): Required. List of items in the Browse Carousel Card. Minimum of two items, maximum of ten. - image_display_options (~.gcd_intent.Intent.Message.BrowseCarouselCard.ImageDisplayOptions): + image_display_options (google.cloud.dialogflow_v2beta1.types.Intent.Message.BrowseCarouselCard.ImageDisplayOptions): Optional. Settings for displaying the image. Applies to every image in [items][google.cloud.dialogflow.v2beta1.Intent.Message.BrowseCarouselCard.items]. @@ -1214,7 +1219,7 @@ class BrowseCarouselCardItem(proto.Message): r"""Browsing carousel tile Attributes: - open_uri_action (~.gcd_intent.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem.OpenUrlAction): + open_uri_action (google.cloud.dialogflow_v2beta1.types.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem.OpenUrlAction): Required. Action to present to the user. title (str): Required. Title of the carousel item. Maximum @@ -1222,7 +1227,7 @@ class BrowseCarouselCardItem(proto.Message): description (str): Optional. Description of the carousel item. Maximum of four lines of text. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2beta1.types.Intent.Message.Image): Optional. Hero image for the carousel item. footer (str): Optional. Text that appears at the bottom of @@ -1236,7 +1241,7 @@ class OpenUrlAction(proto.Message): Attributes: url (str): Required. URL - url_type_hint (~.gcd_intent.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem.OpenUrlAction.UrlTypeHint): + url_type_hint (google.cloud.dialogflow_v2beta1.types.Intent.Message.BrowseCarouselCard.BrowseCarouselCardItem.OpenUrlAction.UrlTypeHint): Optional. Specifies the type of viewer that is used when opening the URL. Defaults to opening via web browser. @@ -1292,15 +1297,15 @@ class TableCard(proto.Message): Required. Title of the card. subtitle (str): Optional. Subtitle to the title. - image (~.gcd_intent.Intent.Message.Image): + image (google.cloud.dialogflow_v2beta1.types.Intent.Message.Image): Optional. Image which should be displayed on the card. - column_properties (Sequence[~.gcd_intent.Intent.Message.ColumnProperties]): + column_properties (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.ColumnProperties]): Optional. Display properties for the columns in this table. - rows (Sequence[~.gcd_intent.Intent.Message.TableCardRow]): + rows (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.TableCardRow]): Optional. Rows in this table of data. - buttons (Sequence[~.gcd_intent.Intent.Message.BasicCard.Button]): + buttons (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.BasicCard.Button]): Optional. List of buttons for the card. """ @@ -1331,7 +1336,7 @@ class ColumnProperties(proto.Message): Attributes: header (str): Required. Column heading. - horizontal_alignment (~.gcd_intent.Intent.Message.ColumnProperties.HorizontalAlignment): + horizontal_alignment (google.cloud.dialogflow_v2beta1.types.Intent.Message.ColumnProperties.HorizontalAlignment): Optional. Defines text alignment for all cells in this column. """ @@ -1356,7 +1361,7 @@ class TableCardRow(proto.Message): [TableCard][google.cloud.dialogflow.v2beta1.Intent.Message.TableCard]. Attributes: - cells (Sequence[~.gcd_intent.Intent.Message.TableCardCell]): + cells (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message.TableCardCell]): Optional. List of cells that make up this row. divider_after (bool): @@ -1538,6 +1543,8 @@ class FollowupIntentInfo(proto.Message): ml_disabled = proto.Field(proto.BOOL, number=19) + live_agent_handoff = proto.Field(proto.BOOL, number=20) + end_interaction = proto.Field(proto.BOOL, number=21) input_context_names = proto.RepeatedField(proto.STRING, number=7) @@ -1587,7 +1594,7 @@ class ListIntentsRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - intent_view (~.gcd_intent.IntentView): + intent_view (google.cloud.dialogflow_v2beta1.types.IntentView): Optional. The resource view to apply to the returned intent. page_size (int): @@ -1615,7 +1622,7 @@ class ListIntentsResponse(proto.Message): [Intents.ListIntents][google.cloud.dialogflow.v2beta1.Intents.ListIntents]. Attributes: - intents (Sequence[~.gcd_intent.Intent]): + intents (Sequence[google.cloud.dialogflow_v2beta1.types.Intent]): The list of agent intents. There will be a maximum number of items returned based on the page_size field in the request. next_page_token (str): @@ -1649,7 +1656,7 @@ class GetIntentRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - intent_view (~.gcd_intent.IntentView): + intent_view (google.cloud.dialogflow_v2beta1.types.IntentView): Optional. The resource view to apply to the returned intent. """ @@ -1672,7 +1679,7 @@ class CreateIntentRequest(proto.Message): - ``projects//agent`` - ``projects//locations//agent`` - intent (~.gcd_intent.Intent): + intent (google.cloud.dialogflow_v2beta1.types.Intent): Required. The intent to create. language_code (str): Optional. The language used to access language-specific @@ -1680,7 +1687,7 @@ class CreateIntentRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - intent_view (~.gcd_intent.IntentView): + intent_view (google.cloud.dialogflow_v2beta1.types.IntentView): Optional. The resource view to apply to the returned intent. """ @@ -1699,7 +1706,7 @@ class UpdateIntentRequest(proto.Message): [Intents.UpdateIntent][google.cloud.dialogflow.v2beta1.Intents.UpdateIntent]. Attributes: - intent (~.gcd_intent.Intent): + intent (google.cloud.dialogflow_v2beta1.types.Intent): Required. The intent to update. language_code (str): Optional. The language used to access language-specific @@ -1707,10 +1714,10 @@ class UpdateIntentRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. - intent_view (~.gcd_intent.IntentView): + intent_view (google.cloud.dialogflow_v2beta1.types.IntentView): Optional. The resource view to apply to the returned intent. """ @@ -1760,7 +1767,7 @@ class BatchUpdateIntentsRequest(proto.Message): format can either be a serialized proto (of IntentBatch type) or JSON object. Note: The URI must start with "gs://". - intent_batch_inline (~.gcd_intent.IntentBatch): + intent_batch_inline (google.cloud.dialogflow_v2beta1.types.IntentBatch): The collection of intents to update or create. language_code (str): @@ -1769,10 +1776,10 @@ class BatchUpdateIntentsRequest(proto.Message): used. For more information, see `Multilingual intent and entity data `__. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. - intent_view (~.gcd_intent.IntentView): + intent_view (google.cloud.dialogflow_v2beta1.types.IntentView): Optional. The resource view to apply to the returned intent. """ @@ -1797,7 +1804,7 @@ class BatchUpdateIntentsResponse(proto.Message): [Intents.BatchUpdateIntents][google.cloud.dialogflow.v2beta1.Intents.BatchUpdateIntents]. Attributes: - intents (Sequence[~.gcd_intent.Intent]): + intents (Sequence[google.cloud.dialogflow_v2beta1.types.Intent]): The collection of updated or created intents. """ @@ -1815,7 +1822,7 @@ class BatchDeleteIntentsRequest(proto.Message): - ``projects//agent`` - ``projects//locations//agent`` - intents (Sequence[~.gcd_intent.Intent]): + intents (Sequence[google.cloud.dialogflow_v2beta1.types.Intent]): Required. The collection of intents to delete. Only intent ``name`` must be filled in. """ @@ -1829,7 +1836,7 @@ class IntentBatch(proto.Message): r"""This message is a wrapper around a collection of intents. Attributes: - intents (Sequence[~.gcd_intent.Intent]): + intents (Sequence[google.cloud.dialogflow_v2beta1.types.Intent]): A collection of intents. """ diff --git a/google/cloud/dialogflow_v2beta1/types/knowledge_base.py b/google/cloud/dialogflow_v2beta1/types/knowledge_base.py index 220e6ff22..aac1458c9 100644 --- a/google/cloud/dialogflow_v2beta1/types/knowledge_base.py +++ b/google/cloud/dialogflow_v2beta1/types/knowledge_base.py @@ -130,7 +130,7 @@ class ListKnowledgeBasesResponse(proto.Message): [KnowledgeBases.ListKnowledgeBases][google.cloud.dialogflow.v2beta1.KnowledgeBases.ListKnowledgeBases]. Attributes: - knowledge_bases (Sequence[~.gcd_knowledge_base.KnowledgeBase]): + knowledge_bases (Sequence[google.cloud.dialogflow_v2beta1.types.KnowledgeBase]): The list of knowledge bases. next_page_token (str): Token to retrieve the next page of results, @@ -170,7 +170,7 @@ class CreateKnowledgeBaseRequest(proto.Message): parent (str): Required. The project to create a knowledge base for. Format: ``projects//locations/``. - knowledge_base (~.gcd_knowledge_base.KnowledgeBase): + knowledge_base (google.cloud.dialogflow_v2beta1.types.KnowledgeBase): Required. The knowledge base to create. """ @@ -203,9 +203,9 @@ class UpdateKnowledgeBaseRequest(proto.Message): [KnowledgeBases.UpdateKnowledgeBase][google.cloud.dialogflow.v2beta1.KnowledgeBases.UpdateKnowledgeBase]. Attributes: - knowledge_base (~.gcd_knowledge_base.KnowledgeBase): + knowledge_base (google.cloud.dialogflow_v2beta1.types.KnowledgeBase): Required. The knowledge base to update. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. Not specified means ``update all``. Currently, only ``display_name`` can be updated, an InvalidArgument will be returned for attempting to update other fields. diff --git a/google/cloud/dialogflow_v2beta1/types/participant.py b/google/cloud/dialogflow_v2beta1/types/participant.py new file mode 100644 index 000000000..d6142d689 --- /dev/null +++ b/google/cloud/dialogflow_v2beta1/types/participant.py @@ -0,0 +1,1530 @@ +# -*- 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.dialogflow_v2beta1.types import audio_config as gcd_audio_config +from google.cloud.dialogflow_v2beta1.types import session +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 google.rpc import status_pb2 as status # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.dialogflow.v2beta1", + manifest={ + "Participant", + "Message", + "CreateParticipantRequest", + "GetParticipantRequest", + "ListParticipantsRequest", + "ListParticipantsResponse", + "UpdateParticipantRequest", + "InputText", + "InputAudio", + "AudioInput", + "OutputAudio", + "AutomatedAgentReply", + "SuggestionFeature", + "AnalyzeContentRequest", + "DtmfParameters", + "AnalyzeContentResponse", + "InputTextConfig", + "StreamingAnalyzeContentRequest", + "StreamingAnalyzeContentResponse", + "AnnotatedMessagePart", + "MessageAnnotation", + "ArticleAnswer", + "FaqAnswer", + "SmartReplyAnswer", + "SuggestionResult", + "SuggestArticlesRequest", + "SuggestArticlesResponse", + "SuggestFaqAnswersRequest", + "SuggestFaqAnswersResponse", + "SuggestSmartRepliesRequest", + "SuggestSmartRepliesResponse", + "Suggestion", + "ListSuggestionsRequest", + "ListSuggestionsResponse", + "CompileSuggestionRequest", + "CompileSuggestionResponse", + "ResponseMessage", + }, +) + + +class Participant(proto.Message): + r"""Represents a conversation participant (human agent, virtual + agent, end-user). + + Attributes: + name (str): + Optional. The unique identifier of this participant. Format: + ``projects//locations//conversations//participants/``. + role (google.cloud.dialogflow_v2beta1.types.Participant.Role): + Immutable. The role this participant plays in + the conversation. This field must be set during + participant creation and is then immutable. + obfuscated_external_user_id (str): + Optional. Obfuscated user id that should be associated with + the created participant. + + You can specify a user id as follows: + + 1. If you set this field in + [CreateParticipantRequest][google.cloud.dialogflow.v2beta1.CreateParticipantRequest.participant] + or + [UpdateParticipantRequest][google.cloud.dialogflow.v2beta1.UpdateParticipantRequest.participant], + Dialogflow adds the obfuscated user id with the + participant. + + 2. If you set this field in + [AnalyzeContent][google.cloud.dialogflow.v2beta1.AnalyzeContentRequest.obfuscated_external_user_id] + or + [StreamingAnalyzeContent][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.obfuscated_external_user_id], + Dialogflow will update + [Participant.obfuscated_external_user_id][google.cloud.dialogflow.v2beta1.Participant.obfuscated_external_user_id]. + + Dialogflow uses this user id for following purposes: + + 1) Billing and measurement. If user with the same + obfuscated_external_user_id is created in a later + conversation, dialogflow will know it's the same user. 2) + Agent assist suggestion personalization. For example, + Dialogflow can use it to provide personalized smart reply + suggestions for this user. + + Note: + + - Please never pass raw user ids to Dialogflow. Always + obfuscate your user id first. + - Dialogflow only accepts a UTF-8 encoded string, e.g., a + hex digest of a hash function like SHA-512. + - The length of the user id must be <= 256 characters. + """ + + class Role(proto.Enum): + r"""Enumeration of the roles a participant can play in a + conversation. + """ + ROLE_UNSPECIFIED = 0 + HUMAN_AGENT = 1 + AUTOMATED_AGENT = 2 + END_USER = 3 + + name = proto.Field(proto.STRING, number=1) + + role = proto.Field(proto.ENUM, number=2, enum=Role,) + + obfuscated_external_user_id = proto.Field(proto.STRING, number=7) + + +class Message(proto.Message): + r"""Represents a message posted into a conversation. + + Attributes: + name (str): + Optional. The unique identifier of the message. Format: + ``projects//locations//conversations//messages/``. + content (str): + Required. The message content. + language_code (str): + Optional. The message language. This should be a + `BCP-47 `__ + language tag. Example: "en-US". + participant (str): + Output only. The participant that sends this + message. + participant_role (google.cloud.dialogflow_v2beta1.types.Participant.Role): + Output only. The role of the participant. + create_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The time when the message was + created in Contact Center AI. + send_time (google.protobuf.timestamp_pb2.Timestamp): + Optional. The time when the message was sent. + message_annotation (google.cloud.dialogflow_v2beta1.types.MessageAnnotation): + Output only. The annotation for the message. + sentiment_analysis (google.cloud.dialogflow_v2beta1.types.SentimentAnalysisResult): + Output only. The sentiment analysis result + for the message. + """ + + name = proto.Field(proto.STRING, number=1) + + content = proto.Field(proto.STRING, number=2) + + language_code = proto.Field(proto.STRING, number=3) + + participant = proto.Field(proto.STRING, number=4) + + participant_role = proto.Field(proto.ENUM, number=5, enum="Participant.Role",) + + create_time = proto.Field(proto.MESSAGE, number=6, message=timestamp.Timestamp,) + + send_time = proto.Field(proto.MESSAGE, number=9, message=timestamp.Timestamp,) + + message_annotation = proto.Field( + proto.MESSAGE, number=7, message="MessageAnnotation", + ) + + sentiment_analysis = proto.Field( + proto.MESSAGE, number=8, message=session.SentimentAnalysisResult, + ) + + +class CreateParticipantRequest(proto.Message): + r"""The request message for + [Participants.CreateParticipant][google.cloud.dialogflow.v2beta1.Participants.CreateParticipant]. + + Attributes: + parent (str): + Required. Resource identifier of the conversation adding the + participant. Format: + ``projects//locations//conversations/``. + participant (google.cloud.dialogflow_v2beta1.types.Participant): + Required. The participant to create. + """ + + parent = proto.Field(proto.STRING, number=1) + + participant = proto.Field(proto.MESSAGE, number=2, message="Participant",) + + +class GetParticipantRequest(proto.Message): + r"""The request message for + [Participants.GetParticipant][google.cloud.dialogflow.v2beta1.Participants.GetParticipant]. + + Attributes: + name (str): + Required. The name of the participant. Format: + ``projects//locations//conversations//participants/``. + """ + + name = proto.Field(proto.STRING, number=1) + + +class ListParticipantsRequest(proto.Message): + r"""The request message for + [Participants.ListParticipants][google.cloud.dialogflow.v2beta1.Participants.ListParticipants]. + + Attributes: + parent (str): + Required. The conversation to list all participants from. + Format: + ``projects//locations//conversations/``. + page_size (int): + Optional. The maximum number of items to + return in a single page. By default 100 and at + most 1000. + page_token (str): + Optional. 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 ListParticipantsResponse(proto.Message): + r"""The response message for + [Participants.ListParticipants][google.cloud.dialogflow.v2beta1.Participants.ListParticipants]. + + Attributes: + participants (Sequence[google.cloud.dialogflow_v2beta1.types.Participant]): + The list of participants. There is 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 + + participants = proto.RepeatedField(proto.MESSAGE, number=1, message="Participant",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class UpdateParticipantRequest(proto.Message): + r"""The request message for + [Participants.UpdateParticipant][google.cloud.dialogflow.v2beta1.Participants.UpdateParticipant]. + + Attributes: + participant (google.cloud.dialogflow_v2beta1.types.Participant): + Required. The participant to update. + update_mask (google.protobuf.field_mask_pb2.FieldMask): + Required. The mask to specify which fields to + update. + """ + + participant = proto.Field(proto.MESSAGE, number=1, message="Participant",) + + update_mask = proto.Field(proto.MESSAGE, number=2, message=field_mask.FieldMask,) + + +class InputText(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 bytes. + language_code (str): + Required. The language of this conversational query. See + `Language + Support `__ + for a list of the currently supported language codes. + """ + + text = proto.Field(proto.STRING, number=1) + + language_code = proto.Field(proto.STRING, number=2) + + +class InputAudio(proto.Message): + r"""Represents the natural language speech audio to be processed. + + Attributes: + config (google.cloud.dialogflow_v2beta1.types.InputAudioConfig): + Required. Instructs the speech recognizer how + to process the speech audio. + audio (bytes): + Required. The natural language speech audio + to be processed. A single request can contain up + to 1 minute of speech audio data. The + transcribed text cannot contain more than 256 + bytes. + """ + + config = proto.Field( + proto.MESSAGE, number=1, message=gcd_audio_config.InputAudioConfig, + ) + + audio = proto.Field(proto.BYTES, number=2) + + +class AudioInput(proto.Message): + r"""Represents the natural language speech audio to be processed. + + Attributes: + config (google.cloud.dialogflow_v2beta1.types.InputAudioConfig): + Required. Instructs the speech recognizer how + to process the speech audio. + audio (bytes): + Required. The natural language speech audio + to be processed. A single request can contain up + to 1 minute of speech audio data. The + transcribed text cannot contain more than 256 + bytes. + """ + + config = proto.Field( + proto.MESSAGE, number=1, message=gcd_audio_config.InputAudioConfig, + ) + + audio = proto.Field(proto.BYTES, number=2) + + +class OutputAudio(proto.Message): + r"""Represents the natural language speech audio to be played to + the end user. + + Attributes: + config (google.cloud.dialogflow_v2beta1.types.OutputAudioConfig): + Required. Instructs the speech synthesizer + how to generate the speech audio. + audio (bytes): + Required. The natural language speech audio. + """ + + config = proto.Field( + proto.MESSAGE, number=1, message=gcd_audio_config.OutputAudioConfig, + ) + + audio = proto.Field(proto.BYTES, number=2) + + +class AutomatedAgentReply(proto.Message): + r"""Represents a response from an automated agent. + + Attributes: + detect_intent_response (google.cloud.dialogflow_v2beta1.types.DetectIntentResponse): + Response of the Dialogflow + [Sessions.DetectIntent][google.cloud.dialogflow.v2beta1.Sessions.DetectIntent] + call. + response_messages (Sequence[google.cloud.dialogflow_v2beta1.types.ResponseMessage]): + Response messages from the automated agent. + intent (str): + Name of the intent if an intent is matched for the query. + For a V2 query, the value format is + ``projects//locations/ /agent/intents/``. + For a V3 query, the value format is + ``projects//locations/ /agents//intents/``. + event (str): + Event name if an event is triggered for the + query. + cx_session_parameters (google.protobuf.struct_pb2.Struct): + The collection of current Dialogflow CX agent + session parameters at the time of this response. + """ + + detect_intent_response = proto.Field( + proto.MESSAGE, number=1, oneof="response", message=session.DetectIntentResponse, + ) + + response_messages = proto.RepeatedField( + proto.MESSAGE, number=3, message="ResponseMessage", + ) + + intent = proto.Field(proto.STRING, number=4, oneof="match") + + event = proto.Field(proto.STRING, number=5, oneof="match") + + cx_session_parameters = proto.Field(proto.MESSAGE, number=6, message=struct.Struct,) + + +class SuggestionFeature(proto.Message): + r"""The type of Human Agent Assistant API suggestion to perform, and the + maximum number of results to return for that type. Multiple + ``Feature`` objects can be specified in the ``features`` list. + + Attributes: + type_ (google.cloud.dialogflow_v2beta1.types.SuggestionFeature.Type): + Type of Human Agent Assistant API feature to + request. + """ + + class Type(proto.Enum): + r"""Defines the type of Human Agent Assistant feature.""" + TYPE_UNSPECIFIED = 0 + ARTICLE_SUGGESTION = 1 + FAQ = 2 + SMART_REPLY = 3 + + type_ = proto.Field(proto.ENUM, number=1, enum=Type,) + + +class AnalyzeContentRequest(proto.Message): + r"""The request message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent]. + + Attributes: + participant (str): + Required. The name of the participant this text comes from. + Format: + ``projects//locations//conversations//participants/``. + text (google.cloud.dialogflow_v2beta1.types.InputText): + The natural language text to be processed. + audio (google.cloud.dialogflow_v2beta1.types.InputAudio): + The natural language speech audio to be + processed. + text_input (google.cloud.dialogflow_v2beta1.types.TextInput): + The natural language text to be processed. + audio_input (google.cloud.dialogflow_v2beta1.types.AudioInput): + The natural language speech audio to be + processed. + event_input (google.cloud.dialogflow_v2beta1.types.EventInput): + An input event to send to Dialogflow. + reply_audio_config (google.cloud.dialogflow_v2beta1.types.OutputAudioConfig): + Speech synthesis configuration. + The speech synthesis settings for a virtual + agent that may be configured for the associated + conversation profile are not used when calling + AnalyzeContent. If this configuration is not + supplied, speech synthesis is disabled. + query_params (google.cloud.dialogflow_v2beta1.types.QueryParameters): + Parameters for a Dialogflow virtual-agent + query. + message_send_time (google.protobuf.timestamp_pb2.Timestamp): + Optional. The send time of the message from + end user or human agent's perspective. It is + used for identifying the same message under one + participant. + + Given two messages under the same participant: + - If send time are different regardless of + whether the content of the messages are exactly + the same, the conversation will regard them as + two distinct messages sent by the participant. + - If send time is the same regardless of whether + the content of the messages are exactly the + same, the conversation will regard them as same + message, and ignore the message received later. + If the value is not provided, a new request will + always be regarded as a new message without any + de-duplication. + request_id (str): + A unique identifier for this request. Restricted to 36 ASCII + characters. A random UUID is recommended. This request is + only idempotent if a ``request_id`` is provided. + """ + + participant = proto.Field(proto.STRING, number=1) + + text = proto.Field(proto.MESSAGE, number=3, oneof="input", message="InputText",) + + audio = proto.Field(proto.MESSAGE, number=4, oneof="input", message="InputAudio",) + + text_input = proto.Field( + proto.MESSAGE, number=6, oneof="input", message=session.TextInput, + ) + + audio_input = proto.Field( + proto.MESSAGE, number=7, oneof="input", message="AudioInput", + ) + + event_input = proto.Field( + proto.MESSAGE, number=8, oneof="input", message=session.EventInput, + ) + + reply_audio_config = proto.Field( + proto.MESSAGE, number=5, message=gcd_audio_config.OutputAudioConfig, + ) + + query_params = proto.Field( + proto.MESSAGE, number=9, message=session.QueryParameters, + ) + + message_send_time = proto.Field( + proto.MESSAGE, number=10, message=timestamp.Timestamp, + ) + + request_id = proto.Field(proto.STRING, number=11) + + +class DtmfParameters(proto.Message): + r"""The message in the response that indicates the parameters of + DTMF. + + Attributes: + accepts_dtmf_input (bool): + Indicates whether DTMF input can be handled + in the next request. + """ + + accepts_dtmf_input = proto.Field(proto.BOOL, number=1) + + +class AnalyzeContentResponse(proto.Message): + r"""The response message for + [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent]. + + Attributes: + reply_text (str): + Output only. The output text content. + This field is set if the automated agent + responded with text to show to the user. + reply_audio (google.cloud.dialogflow_v2beta1.types.OutputAudio): + Optional. The audio data bytes encoded as specified in the + request. This field is set if: + + - ``reply_audio_config`` was specified in the request, or + - The automated agent responded with audio to play to the + user. In such case, ``reply_audio.config`` contains + settings used to synthesize the speech. + + 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. + automated_agent_reply (google.cloud.dialogflow_v2beta1.types.AutomatedAgentReply): + Optional. Only set if a Dialogflow automated agent has + responded. Note that: + [AutomatedAgentReply.detect_intent_response.output_audio][] + and + [AutomatedAgentReply.detect_intent_response.output_audio_config][] + are always empty, use + [reply_audio][google.cloud.dialogflow.v2beta1.AnalyzeContentResponse.reply_audio] + instead. + message (google.cloud.dialogflow_v2beta1.types.Message): + Output only. Message analyzed by CCAI. + human_agent_suggestion_results (Sequence[google.cloud.dialogflow_v2beta1.types.SuggestionResult]): + The suggestions for most recent human agent. The order is + the same as + [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] + of + [HumanAgentAssistantConfig.human_agent_suggestion_config][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.human_agent_suggestion_config]. + end_user_suggestion_results (Sequence[google.cloud.dialogflow_v2beta1.types.SuggestionResult]): + The suggestions for end user. The order is the same as + [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] + of + [HumanAgentAssistantConfig.end_user_suggestion_config][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.end_user_suggestion_config]. + dtmf_parameters (google.cloud.dialogflow_v2beta1.types.DtmfParameters): + Indicates the parameters of DTMF. + """ + + reply_text = proto.Field(proto.STRING, number=1) + + reply_audio = proto.Field(proto.MESSAGE, number=2, message="OutputAudio",) + + automated_agent_reply = proto.Field( + proto.MESSAGE, number=3, message="AutomatedAgentReply", + ) + + message = proto.Field(proto.MESSAGE, number=5, message="Message",) + + human_agent_suggestion_results = proto.RepeatedField( + proto.MESSAGE, number=6, message="SuggestionResult", + ) + + end_user_suggestion_results = proto.RepeatedField( + proto.MESSAGE, number=7, message="SuggestionResult", + ) + + dtmf_parameters = proto.Field(proto.MESSAGE, number=9, message="DtmfParameters",) + + +class InputTextConfig(proto.Message): + r"""Defines the language used in the input text. + + Attributes: + language_code (str): + Required. The language of this conversational query. See + `Language + Support `__ + for a list of the currently supported language codes. + """ + + language_code = proto.Field(proto.STRING, number=1) + + +class StreamingAnalyzeContentRequest(proto.Message): + r"""The top-level message sent by the client to the + [Participants.StreamingAnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.StreamingAnalyzeContent] + method. + + Multiple request messages should be sent in order: + + 1. The first message must contain + [participant][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.participant], + [config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] + and optionally + [query_params][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.query_params]. + If you want to receive an audio response, it should also contain + [reply_audio_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.reply_audio_config]. + The message must not contain + [input][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input]. + + 2. If + [config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] + in the first message was set to + [audio_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.audio_config], + all subsequent messages must contain + [input_audio][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_audio] + to continue with Speech recognition. If you decide to rather + analyze text input after you already started Speech recognition, + please send a message with + [StreamingAnalyzeContentRequest.input_text][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_text]. + + However, note that: + + - Dialogflow will bill you for the audio so far. + - Dialogflow discards all Speech recognition results in favor of + the text input. + + 3. If + [StreamingAnalyzeContentRequest.config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.config] + in the first message was set to + [StreamingAnalyzeContentRequest.text_config][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.text_config], + then the second message must contain only + [input_text][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentRequest.input_text]. + Moreover, you must not send more than two messages. + + After you sent all input, you must half-close or abort the request + stream. + + Attributes: + participant (str): + Required. The name of the participant this text comes from. + Format: + ``projects//locations//conversations//participants/``. + audio_config (google.cloud.dialogflow_v2beta1.types.InputAudioConfig): + Instructs the speech recognizer how to + process the speech audio. + text_config (google.cloud.dialogflow_v2beta1.types.InputTextConfig): + The natural language text to be processed. + reply_audio_config (google.cloud.dialogflow_v2beta1.types.OutputAudioConfig): + Speech synthesis configuration. + The speech synthesis settings for a virtual + agent that may be configured for the associated + conversation profile are not used when calling + StreamingAnalyzeContent. If this configuration + is not supplied, speech synthesis is disabled. + input_audio (bytes): + The input audio content to be recognized. Must be sent if + ``audio_config`` is set in the first message. The complete + audio over all streaming messages must not exceed 1 minute. + input_text (str): + The UTF-8 encoded natural language text to be processed. + Must be sent if ``text_config`` is set in the first message. + Text length must not exceed 256 bytes. The ``input_text`` + field can be only sent once. + input_dtmf (google.cloud.dialogflow_v2beta1.types.TelephonyDtmfEvents): + The DTMF digits used to invoke intent and + fill in parameter value. + This input is ignored if the previous response + indicated that DTMF input is not accepted. + query_params (google.cloud.dialogflow_v2beta1.types.QueryParameters): + Parameters for a Dialogflow virtual-agent + query. + enable_extended_streaming (bool): + Enable full bidirectional streaming. You can keep streaming + the audio until timeout, and there's no need to half close + the stream to get the response. + + Restrictions: + + - Timeout: 3 mins. + - Audio Encoding: only supports + [AudioEncoding.AUDIO_ENCODING_LINEAR_16][google.cloud.dialogflow.v2beta1.AudioEncoding.AUDIO_ENCODING_LINEAR_16] + and + [AudioEncoding.AUDIO_ENCODING_MULAW][google.cloud.dialogflow.v2beta1.AudioEncoding.AUDIO_ENCODING_MULAW] + - Lifecycle: conversation should be in ``Assist Stage``, go + to [Conversation.CreateConversation][] for more + information. + + InvalidArgument Error will be returned if the one of + restriction checks failed. + + You can find more details in + https://cloud.google.com/dialogflow/priv/docs/agent-assist/analyze-content-streaming + """ + + participant = proto.Field(proto.STRING, number=1) + + audio_config = proto.Field( + proto.MESSAGE, + number=2, + oneof="config", + message=gcd_audio_config.InputAudioConfig, + ) + + text_config = proto.Field( + proto.MESSAGE, number=3, oneof="config", message="InputTextConfig", + ) + + reply_audio_config = proto.Field( + proto.MESSAGE, number=4, message=gcd_audio_config.OutputAudioConfig, + ) + + input_audio = proto.Field(proto.BYTES, number=5, oneof="input") + + input_text = proto.Field(proto.STRING, number=6, oneof="input") + + input_dtmf = proto.Field( + proto.MESSAGE, + number=9, + oneof="input", + message=gcd_audio_config.TelephonyDtmfEvents, + ) + + query_params = proto.Field( + proto.MESSAGE, number=7, message=session.QueryParameters, + ) + + enable_extended_streaming = proto.Field(proto.BOOL, number=11) + + +class StreamingAnalyzeContentResponse(proto.Message): + r"""The top-level message returned from the ``StreamingAnalyzeContent`` + 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 next message contains ``reply_text`` and optionally + ``reply_audio`` returned by an agent. This message may also + contain ``automated_agent_reply``. + + Attributes: + recognition_result (google.cloud.dialogflow_v2beta1.types.StreamingRecognitionResult): + The result of speech recognition. + reply_text (str): + Optional. The output text content. + This field is set if an automated agent + responded with a text for the user. + reply_audio (google.cloud.dialogflow_v2beta1.types.OutputAudio): + Optional. The audio data bytes encoded as specified in the + request. This field is set if: + + - The ``reply_audio_config`` field is specified in the + request. + - The automated agent, which this output comes from, + responded with audio. In such case, the + ``reply_audio.config`` field contains settings used to + synthesize the speech. + + 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. + automated_agent_reply (google.cloud.dialogflow_v2beta1.types.AutomatedAgentReply): + Optional. Only set if a Dialogflow automated agent has + responded. Note that: + [AutomatedAgentReply.detect_intent_response.output_audio][] + and + [AutomatedAgentReply.detect_intent_response.output_audio_config][] + are always empty, use + [reply_audio][google.cloud.dialogflow.v2beta1.StreamingAnalyzeContentResponse.reply_audio] + instead. + message (google.cloud.dialogflow_v2beta1.types.Message): + Output only. Message analyzed by CCAI. + human_agent_suggestion_results (Sequence[google.cloud.dialogflow_v2beta1.types.SuggestionResult]): + The suggestions for most recent human agent. The order is + the same as + [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] + of + [HumanAgentAssistantConfig.human_agent_suggestion_config][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.human_agent_suggestion_config]. + end_user_suggestion_results (Sequence[google.cloud.dialogflow_v2beta1.types.SuggestionResult]): + The suggestions for end user. The order is the same as + [HumanAgentAssistantConfig.SuggestionConfig.feature_configs][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.SuggestionConfig.feature_configs] + of + [HumanAgentAssistantConfig.end_user_suggestion_config][google.cloud.dialogflow.v2beta1.HumanAgentAssistantConfig.end_user_suggestion_config]. + dtmf_parameters (google.cloud.dialogflow_v2beta1.types.DtmfParameters): + Indicates the parameters of DTMF. + """ + + recognition_result = proto.Field( + proto.MESSAGE, number=1, message=session.StreamingRecognitionResult, + ) + + reply_text = proto.Field(proto.STRING, number=2) + + reply_audio = proto.Field(proto.MESSAGE, number=3, message="OutputAudio",) + + automated_agent_reply = proto.Field( + proto.MESSAGE, number=4, message="AutomatedAgentReply", + ) + + message = proto.Field(proto.MESSAGE, number=6, message="Message",) + + human_agent_suggestion_results = proto.RepeatedField( + proto.MESSAGE, number=7, message="SuggestionResult", + ) + + end_user_suggestion_results = proto.RepeatedField( + proto.MESSAGE, number=8, message="SuggestionResult", + ) + + dtmf_parameters = proto.Field(proto.MESSAGE, number=10, message="DtmfParameters",) + + +class AnnotatedMessagePart(proto.Message): + r"""Represents a part of a message possibly annotated with an + entity. The part can be an entity or purely a part of the + message between two entities or message start/end. + + Attributes: + text (str): + Required. A part of a message possibly + annotated with an entity. + entity_type (str): + Optional. The `Dialogflow system entity + type `__ + of this message part. If this is empty, Dialogflow could not + annotate the phrase part with a system entity. + formatted_value (google.protobuf.struct_pb2.Value): + Optional. The `Dialogflow system entity formatted + value `__ + of this message part. For example for a system entity of + type ``@sys.unit-currency``, this may contain: + + .. raw:: html + +
+                {
+                  "amount": 5,
+                  "currency": "USD"
+                }
+                
+ """ + + text = proto.Field(proto.STRING, number=1) + + entity_type = proto.Field(proto.STRING, number=2) + + formatted_value = proto.Field(proto.MESSAGE, number=3, message=struct.Value,) + + +class MessageAnnotation(proto.Message): + r"""Represents the result of annotation for the message. + + Attributes: + parts (Sequence[google.cloud.dialogflow_v2beta1.types.AnnotatedMessagePart]): + Optional. The collection of annotated message parts ordered + by their position in the message. You can recover the + annotated message by concatenating + [AnnotatedMessagePart.text]. + contain_entities (bool): + Required. Indicates whether the text message + contains entities. + """ + + parts = proto.RepeatedField( + proto.MESSAGE, number=1, message="AnnotatedMessagePart", + ) + + contain_entities = proto.Field(proto.BOOL, number=2) + + +class ArticleAnswer(proto.Message): + r"""Represents article answer. + + Attributes: + title (str): + The article title. + uri (str): + The article URI. + snippets (Sequence[str]): + Output only. Article snippets. + metadata (Sequence[google.cloud.dialogflow_v2beta1.types.ArticleAnswer.MetadataEntry]): + A map that contains metadata about the answer + and the document from which it originates. + answer_record (str): + The name of answer record, in the format of + "projects//locations//answerRecords/". + """ + + title = proto.Field(proto.STRING, number=1) + + uri = proto.Field(proto.STRING, number=2) + + snippets = proto.RepeatedField(proto.STRING, number=3) + + metadata = proto.MapField(proto.STRING, proto.STRING, number=5) + + answer_record = proto.Field(proto.STRING, number=6) + + +class FaqAnswer(proto.Message): + r"""Represents answer from "frequently asked questions". + + Attributes: + answer (str): + The piece of text from the ``source`` knowledge base + document. + confidence (float): + The system's confidence score that this + Knowledge answer is a good match for this + conversational query, range from 0.0 (completely + uncertain) to 1.0 (completely certain). + question (str): + The corresponding FAQ question. + source (str): + Indicates which Knowledge Document this answer was extracted + from. Format: + ``projects//locations//agent/knowledgeBases//documents/``. + metadata (Sequence[google.cloud.dialogflow_v2beta1.types.FaqAnswer.MetadataEntry]): + A map that contains metadata about the answer + and the document from which it originates. + answer_record (str): + The name of answer record, in the format of + "projects//locations//answerRecords/". + """ + + answer = proto.Field(proto.STRING, number=1) + + confidence = proto.Field(proto.FLOAT, number=2) + + question = proto.Field(proto.STRING, number=3) + + source = proto.Field(proto.STRING, number=4) + + metadata = proto.MapField(proto.STRING, proto.STRING, number=5) + + answer_record = proto.Field(proto.STRING, number=6) + + +class SmartReplyAnswer(proto.Message): + r"""Represents a smart reply answer. + + Attributes: + reply (str): + The content of the reply. + confidence (float): + Smart reply confidence. + The system's confidence score that this reply is + a good match for this conversation, as a value + from 0.0 (completely uncertain) to 1.0 + (completely certain). + answer_record (str): + The name of answer record, in the format of + "projects//locations//answerRecords/". + """ + + reply = proto.Field(proto.STRING, number=1) + + confidence = proto.Field(proto.FLOAT, number=2) + + answer_record = proto.Field(proto.STRING, number=3) + + +class SuggestionResult(proto.Message): + r"""One response of different type of suggestion response which is used + in the response of + [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent] + and + [Participants.AnalyzeContent][google.cloud.dialogflow.v2beta1.Participants.AnalyzeContent], + as well as + [HumanAgentAssistantEvent][google.cloud.dialogflow.v2beta1.HumanAgentAssistantEvent]. + + Attributes: + error (google.rpc.status_pb2.Status): + Error status if the request failed. + suggest_articles_response (google.cloud.dialogflow_v2beta1.types.SuggestArticlesResponse): + SuggestArticlesResponse if request is for + ARTICLE_SUGGESTION. + suggest_faq_answers_response (google.cloud.dialogflow_v2beta1.types.SuggestFaqAnswersResponse): + SuggestFaqAnswersResponse if request is for FAQ_ANSWER. + suggest_smart_replies_response (google.cloud.dialogflow_v2beta1.types.SuggestSmartRepliesResponse): + SuggestSmartRepliesResponse if request is for SMART_REPLY. + """ + + error = proto.Field( + proto.MESSAGE, number=1, oneof="suggestion_response", message=status.Status, + ) + + suggest_articles_response = proto.Field( + proto.MESSAGE, + number=2, + oneof="suggestion_response", + message="SuggestArticlesResponse", + ) + + suggest_faq_answers_response = proto.Field( + proto.MESSAGE, + number=3, + oneof="suggestion_response", + message="SuggestFaqAnswersResponse", + ) + + suggest_smart_replies_response = proto.Field( + proto.MESSAGE, + number=4, + oneof="suggestion_response", + message="SuggestSmartRepliesResponse", + ) + + +class SuggestArticlesRequest(proto.Message): + r"""The request message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles]. + + Attributes: + parent (str): + Required. The name of the participant to fetch suggestion + for. Format: + ``projects//locations//conversations//participants/``. + latest_message (str): + Optional. The name of the latest conversation message to + compile suggestion for. If empty, it will be the latest + message of the conversation. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Optional. Max number of messages prior to and including + [latest_message][google.cloud.dialogflow.v2beta1.SuggestArticlesRequest.latest_message] + to use as context when compiling the suggestion. By default + 20 and at most 50. + """ + + parent = proto.Field(proto.STRING, number=1) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class SuggestArticlesResponse(proto.Message): + r"""The response message for + [Participants.SuggestArticles][google.cloud.dialogflow.v2beta1.Participants.SuggestArticles]. + + Attributes: + article_answers (Sequence[google.cloud.dialogflow_v2beta1.types.ArticleAnswer]): + Output only. Articles ordered by score in + descending order. + latest_message (str): + The name of the latest conversation message used to compile + suggestion for. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Number of messages prior to and including + [latest_message][google.cloud.dialogflow.v2beta1.SuggestArticlesResponse.latest_message] + to compile the suggestion. It may be smaller than the + [SuggestArticlesResponse.context_size][google.cloud.dialogflow.v2beta1.SuggestArticlesResponse.context_size] + field in the request if there aren't that many messages in + the conversation. + """ + + article_answers = proto.RepeatedField( + proto.MESSAGE, number=1, message="ArticleAnswer", + ) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class SuggestFaqAnswersRequest(proto.Message): + r"""The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers]. + + Attributes: + parent (str): + Required. The name of the participant to fetch suggestion + for. Format: + ``projects//locations//conversations//participants/``. + latest_message (str): + Optional. The name of the latest conversation message to + compile suggestion for. If empty, it will be the latest + message of the conversation. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Optional. Max number of messages prior to and including + [latest_message] to use as context when compiling the + suggestion. By default 20 and at most 50. + """ + + parent = proto.Field(proto.STRING, number=1) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class SuggestFaqAnswersResponse(proto.Message): + r"""The request message for + [Participants.SuggestFaqAnswers][google.cloud.dialogflow.v2beta1.Participants.SuggestFaqAnswers]. + + Attributes: + faq_answers (Sequence[google.cloud.dialogflow_v2beta1.types.FaqAnswer]): + Output only. Answers extracted from FAQ + documents. + latest_message (str): + The name of the latest conversation message used to compile + suggestion for. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Number of messages prior to and including + [latest_message][google.cloud.dialogflow.v2beta1.SuggestFaqAnswersResponse.latest_message] + to compile the suggestion. It may be smaller than the + [SuggestFaqAnswersRequest.context_size][google.cloud.dialogflow.v2beta1.SuggestFaqAnswersRequest.context_size] + field in the request if there aren't that many messages in + the conversation. + """ + + faq_answers = proto.RepeatedField(proto.MESSAGE, number=1, message="FaqAnswer",) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class SuggestSmartRepliesRequest(proto.Message): + r"""The request message for + [Participants.SuggestSmartReplies][google.cloud.dialogflow.v2beta1.Participants.SuggestSmartReplies]. + + Attributes: + parent (str): + Required. The name of the participant to fetch suggestion + for. Format: + ``projects//locations//conversations//participants/``. + current_text_input (google.cloud.dialogflow_v2beta1.types.TextInput): + The current natural language text segment to + compile suggestion for. This provides a way for + user to get follow up smart reply suggestion + after a smart reply selection, without sending a + text message. + latest_message (str): + The name of the latest conversation message to compile + suggestion for. If empty, it will be the latest message of + the conversation. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Optional. Max number of messages prior to and including + [latest_message] to use as context when compiling the + suggestion. By default 20 and at most 50. + """ + + parent = proto.Field(proto.STRING, number=1) + + current_text_input = proto.Field( + proto.MESSAGE, number=4, message=session.TextInput, + ) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class SuggestSmartRepliesResponse(proto.Message): + r"""The response message for + [Participants.SuggestSmartReplies][google.cloud.dialogflow.v2beta1.Participants.SuggestSmartReplies]. + + Attributes: + smart_reply_answers (Sequence[google.cloud.dialogflow_v2beta1.types.SmartReplyAnswer]): + Output only. Multiple reply options provided + by smart reply service. The order is based on + the rank of the model prediction. The maximum + number of the returned replies is set in + SmartReplyConfig. + latest_message (str): + The name of the latest conversation message used to compile + suggestion for. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Number of messages prior to and including + [latest_message][google.cloud.dialogflow.v2beta1.SuggestSmartRepliesResponse.latest_message] + to compile the suggestion. It may be smaller than the + [SuggestSmartRepliesRequest.context_size][google.cloud.dialogflow.v2beta1.SuggestSmartRepliesRequest.context_size] + field in the request if there aren't that many messages in + the conversation. + """ + + smart_reply_answers = proto.RepeatedField( + proto.MESSAGE, number=1, message="SmartReplyAnswer", + ) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class Suggestion(proto.Message): + r"""Represents a suggestion for a human agent. + + Attributes: + name (str): + Output only. The name of this suggestion. Format: + ``projects//locations//conversations//participants/*/suggestions/``. + articles (Sequence[google.cloud.dialogflow_v2beta1.types.Suggestion.Article]): + Output only. Articles ordered by score in + descending order. + faq_answers (Sequence[google.cloud.dialogflow_v2beta1.types.Suggestion.FaqAnswer]): + Output only. Answers extracted from FAQ + documents. + create_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The time the suggestion was + created. + latest_message (str): + Output only. Latest message used as context to compile this + suggestion. + + Format: + ``projects//locations//conversations//messages/``. + """ + + class Article(proto.Message): + r"""Represents suggested article. + + Attributes: + title (str): + Output only. The article title. + uri (str): + Output only. The article URI. + snippets (Sequence[str]): + Output only. Article snippets. + metadata (Sequence[google.cloud.dialogflow_v2beta1.types.Suggestion.Article.MetadataEntry]): + Output only. A map that contains metadata + about the answer and the document from which it + originates. + answer_record (str): + Output only. The name of answer record, in + the format of "projects//locations//answerRecords/". + """ + + title = proto.Field(proto.STRING, number=1) + + uri = proto.Field(proto.STRING, number=2) + + snippets = proto.RepeatedField(proto.STRING, number=3) + + metadata = proto.MapField(proto.STRING, proto.STRING, number=5) + + answer_record = proto.Field(proto.STRING, number=6) + + class FaqAnswer(proto.Message): + r"""Represents suggested answer from "frequently asked + questions". + + Attributes: + answer (str): + Output only. The piece of text from the ``source`` knowledge + base document. + confidence (float): + The system's confidence score that this + Knowledge answer is a good match for this + conversational query, range from 0.0 (completely + uncertain) to 1.0 (completely certain). + question (str): + Output only. The corresponding FAQ question. + source (str): + Output only. Indicates which Knowledge Document this answer + was extracted from. Format: + ``projects//locations//agent/knowledgeBases//documents/``. + metadata (Sequence[google.cloud.dialogflow_v2beta1.types.Suggestion.FaqAnswer.MetadataEntry]): + Output only. A map that contains metadata + about the answer and the document from which it + originates. + answer_record (str): + Output only. The name of answer record, in + the format of "projects//locations//answerRecords/". + """ + + answer = proto.Field(proto.STRING, number=1) + + confidence = proto.Field(proto.FLOAT, number=2) + + question = proto.Field(proto.STRING, number=3) + + source = proto.Field(proto.STRING, number=4) + + metadata = proto.MapField(proto.STRING, proto.STRING, number=5) + + answer_record = proto.Field(proto.STRING, number=6) + + name = proto.Field(proto.STRING, number=1) + + articles = proto.RepeatedField(proto.MESSAGE, number=2, message=Article,) + + faq_answers = proto.RepeatedField(proto.MESSAGE, number=4, message=FaqAnswer,) + + create_time = proto.Field(proto.MESSAGE, number=5, message=timestamp.Timestamp,) + + latest_message = proto.Field(proto.STRING, number=7) + + +class ListSuggestionsRequest(proto.Message): + r"""The request message for + [Participants.ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions]. + + Attributes: + parent (str): + Required. The name of the participant to fetch suggestions + for. Format: + ``projects//locations//conversations//participants/``. + page_size (int): + Optional. The maximum number of items to + return in a single page. The default value is + 100; the maximum value is 1000. + page_token (str): + Optional. The next_page_token value returned from a previous + list request. + filter (str): + Optional. Filter on suggestions fields. Currently predicates + on ``create_time`` and ``create_time_epoch_microseconds`` + are supported. ``create_time`` only support milliseconds + accuracy. E.g., + ``create_time_epoch_microseconds > 1551790877964485`` or + ``create_time > 2017-01-15T01:30:15.01Z`` + + For more information about filtering, see `API + Filtering `__. + """ + + parent = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + filter = proto.Field(proto.STRING, number=4) + + +class ListSuggestionsResponse(proto.Message): + r"""The response message for + [Participants.ListSuggestions][google.cloud.dialogflow.v2beta1.Participants.ListSuggestions]. + + Attributes: + suggestions (Sequence[google.cloud.dialogflow_v2beta1.types.Suggestion]): + Required. The list of suggestions. There will be a maximum + number of items returned based on the page_size field in the + request. ``suggestions`` is sorted by ``create_time`` in + descending order. + next_page_token (str): + Optional. 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 + + suggestions = proto.RepeatedField(proto.MESSAGE, number=1, message="Suggestion",) + + next_page_token = proto.Field(proto.STRING, number=2) + + +class CompileSuggestionRequest(proto.Message): + r"""The request message for + [Participants.CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion]. + + Attributes: + parent (str): + Required. The name of the participant to fetch suggestion + for. Format: + ``projects//locations//conversations//participants/``. + latest_message (str): + Optional. The name of the latest conversation message to + compile suggestion for. If empty, it will be the latest + message of the conversation. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Optional. Max number of messages prior to and including + [latest_message] to use as context when compiling the + suggestion. If zero or less than zero, 20 is used. + """ + + parent = proto.Field(proto.STRING, number=1) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class CompileSuggestionResponse(proto.Message): + r"""The response message for + [Participants.CompileSuggestion][google.cloud.dialogflow.v2beta1.Participants.CompileSuggestion]. + + Attributes: + suggestion (google.cloud.dialogflow_v2beta1.types.Suggestion): + The compiled suggestion. + latest_message (str): + The name of the latest conversation message used to compile + suggestion for. + + Format: + ``projects//locations//conversations//messages/``. + context_size (int): + Number of messages prior to and including + [latest_message][google.cloud.dialogflow.v2beta1.CompileSuggestionResponse.latest_message] + to compile the suggestion. It may be smaller than the + [CompileSuggestionRequest.context_size][google.cloud.dialogflow.v2beta1.CompileSuggestionRequest.context_size] + field in the request if there aren't that many messages in + the conversation. + """ + + suggestion = proto.Field(proto.MESSAGE, number=1, message="Suggestion",) + + latest_message = proto.Field(proto.STRING, number=2) + + context_size = proto.Field(proto.INT32, number=3) + + +class ResponseMessage(proto.Message): + r"""Response messages from an automated agent. + + Attributes: + text (google.cloud.dialogflow_v2beta1.types.ResponseMessage.Text): + Returns a text response. + payload (google.protobuf.struct_pb2.Struct): + Returns a response containing a custom, + platform-specific payload. + live_agent_handoff (google.cloud.dialogflow_v2beta1.types.ResponseMessage.LiveAgentHandoff): + Hands off conversation to a live agent. + end_interaction (google.cloud.dialogflow_v2beta1.types.ResponseMessage.EndInteraction): + A signal that indicates the interaction with + the Dialogflow agent has ended. + """ + + class Text(proto.Message): + r"""The text response message. + + Attributes: + text (Sequence[str]): + A collection of text responses. + """ + + text = proto.RepeatedField(proto.STRING, number=1) + + class LiveAgentHandoff(proto.Message): + r"""Indicates that the conversation should be handed off to a human + 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 of a CX 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 (google.protobuf.struct_pb2.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 EndInteraction(proto.Message): + r"""Indicates that interaction with the Dialogflow agent has + ended. + """ + + text = proto.Field(proto.MESSAGE, number=1, oneof="message", message=Text,) + + payload = proto.Field( + proto.MESSAGE, number=2, oneof="message", message=struct.Struct, + ) + + live_agent_handoff = proto.Field( + proto.MESSAGE, number=3, oneof="message", message=LiveAgentHandoff, + ) + + end_interaction = proto.Field( + proto.MESSAGE, number=4, oneof="message", message=EndInteraction, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/dialogflow_v2beta1/types/session.py b/google/cloud/dialogflow_v2beta1/types/session.py index 6e1bb457f..435e12e36 100644 --- a/google/cloud/dialogflow_v2beta1/types/session.py +++ b/google/cloud/dialogflow_v2beta1/types/session.py @@ -66,21 +66,22 @@ class DetectIntentRequest(proto.Message): If ``Location ID`` is not specified we assume default 'us' location. If ``Environment ID`` is not specified, we assume - default 'draft' environment. If ``User ID`` is not - specified, we are using "-". It's up to the API caller to - choose an appropriate ``Session ID`` and ``User Id``. They - can be a random number or some type of user and session - identifiers (preferably hashed). The length of the - ``Session ID`` and ``User ID`` must not exceed 36 + default 'draft' environment (``Environment ID`` might be + referred to as environment name at some places). If + ``User ID`` is not specified, we are using "-". It's up to + the API caller to choose an appropriate ``Session ID`` and + ``User Id``. They can be a random number or some type of + user and session identifiers (preferably hashed). The length + of the ``Session ID`` and ``User ID`` must not exceed 36 characters. For more information, see the `API interactions guide `__. Note: Always use agent versions for production traffic. See `Versions and environments `__. - query_params (~.gcd_session.QueryParameters): + query_params (google.cloud.dialogflow_v2beta1.types.QueryParameters): The parameters of this query. - query_input (~.gcd_session.QueryInput): + query_input (google.cloud.dialogflow_v2beta1.types.QueryInput): Required. The input specification. It can be set to: 1. an audio config @@ -90,12 +91,12 @@ class DetectIntentRequest(proto.Message): or 3. an event that specifies which intent to trigger. - output_audio_config (~.gcd_audio_config.OutputAudioConfig): + output_audio_config (google.cloud.dialogflow_v2beta1.types.OutputAudioConfig): Instructs the speech synthesizer how to generate the output audio. If this field is not set and agent-level speech synthesizer is not configured, no output audio is generated. - output_audio_config_mask (~.field_mask.FieldMask): + output_audio_config_mask (google.protobuf.field_mask_pb2.FieldMask): Mask for [output_audio_config][google.cloud.dialogflow.v2beta1.DetectIntentRequest.output_audio_config] indicating which settings in this request-level config @@ -137,11 +138,11 @@ class DetectIntentResponse(proto.Message): 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 (~.gcd_session.QueryResult): + query_result (google.cloud.dialogflow_v2beta1.types.QueryResult): The selected results of the conversational query or event processing. See ``alternative_query_results`` for additional potential results. - alternative_query_results (Sequence[~.gcd_session.QueryResult]): + alternative_query_results (Sequence[google.cloud.dialogflow_v2beta1.types.QueryResult]): If Knowledge Connectors are enabled, there could be more than one result returned for a given query or event, and this field will contain all results except for the top one, @@ -151,7 +152,7 @@ class DetectIntentResponse(proto.Message): Connectors are disabled, this field will be empty until multiple responses for regular intents are supported, at which point those additional results will be surfaced here. - webhook_status (~.status.Status): + webhook_status (google.rpc.status_pb2.Status): Specifies the status of the webhook request. output_audio (bytes): The audio data bytes encoded as specified in the request. @@ -165,7 +166,7 @@ class DetectIntentResponse(proto.Message): 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 (~.gcd_audio_config.OutputAudioConfig): + output_audio_config (google.cloud.dialogflow_v2beta1.types.OutputAudioConfig): The config used by the speech synthesizer to generate the output audio. """ @@ -196,22 +197,22 @@ class QueryParameters(proto.Message): zone database `__, e.g., America/New_York, Europe/Paris. If not provided, the time zone specified in agent settings is used. - geo_location (~.latlng.LatLng): + geo_location (google.type.latlng_pb2.LatLng): The geo location of this conversational query. - contexts (Sequence[~.context.Context]): + contexts (Sequence[google.cloud.dialogflow_v2beta1.types.Context]): The collection of contexts to be activated before this query is executed. reset_contexts (bool): Specifies whether to delete all contexts in the current session before the new ones are activated. - session_entity_types (Sequence[~.session_entity_type.SessionEntityType]): + session_entity_types (Sequence[google.cloud.dialogflow_v2beta1.types.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): + payload (google.protobuf.struct_pb2.Struct): This field can be used to pass custom data to your webhook. Arbitrary JSON objects are supported. If supplied, the value is used to populate the @@ -222,20 +223,20 @@ class QueryParameters(proto.Message): the KnowledgeBases enabled in the agent (through UI) will be used. Format: ``projects//knowledgeBases/``. - sentiment_analysis_request_config (~.gcd_session.SentimentAnalysisRequestConfig): + sentiment_analysis_request_config (google.cloud.dialogflow_v2beta1.types.SentimentAnalysisRequestConfig): Configures the type of sentiment analysis to perform. If not provided, sentiment analysis is not performed. Note: Sentiment Analysis is only currently available for Essentials Edition agents. - sub_agents (Sequence[~.agent.SubAgent]): + sub_agents (Sequence[google.cloud.dialogflow_v2beta1.types.SubAgent]): For mega agent query, directly specify which sub agents to query. If any specified sub agent is not linked to the mega agent, an error will be returned. If empty, Dialogflow will decide which sub agents to query. If specified for a non-mega-agent query, will be silently ignored. - webhook_headers (Sequence[~.gcd_session.QueryParameters.WebhookHeadersEntry]): + webhook_headers (Sequence[google.cloud.dialogflow_v2beta1.types.QueryParameters.WebhookHeadersEntry]): This field can be used to pass HTTP headers for a webhook call. These headers will be sent to webhook along with the headers that have been @@ -285,12 +286,12 @@ class QueryInput(proto.Message): 3. An event that specifies which intent to trigger. Attributes: - audio_config (~.gcd_audio_config.InputAudioConfig): + audio_config (google.cloud.dialogflow_v2beta1.types.InputAudioConfig): Instructs the speech recognizer how to process the speech audio. - text (~.gcd_session.TextInput): + text (google.cloud.dialogflow_v2beta1.types.TextInput): The natural language text to be processed. - event (~.gcd_session.EventInput): + event (google.cloud.dialogflow_v2beta1.types.EventInput): The event to be processed. """ @@ -342,7 +343,7 @@ class QueryResult(proto.Message): StreamingRecognitionResult. action (str): The action name from the matched intent. - parameters (~.struct.Struct): + parameters (google.protobuf.struct_pb2.Struct): The collection of extracted parameters. Depending on your protocol or client library language, this is a map, associative array, @@ -353,9 +354,10 @@ class QueryResult(proto.Message): - 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: + composite entity: map - Else: depending on + parameter value type, could be one of string, + number, boolean, null, list or map + - MapValue value: - If parameter's entity type is a composite entity: map from composite entity property names to property values - @@ -373,23 +375,23 @@ class QueryResult(proto.Message): The text to be pronounced to the user or shown on the screen. Note: This is a legacy field, ``fulfillment_messages`` should be preferred. - fulfillment_messages (Sequence[~.gcd_intent.Intent.Message]): + fulfillment_messages (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message]): The collection of rich messages to present to the user. webhook_source (str): If the query was fulfilled by a webhook call, this field is set to the value of the ``source`` field returned in the webhook response. - webhook_payload (~.struct.Struct): + webhook_payload (google.protobuf.struct_pb2.Struct): If the query was fulfilled by a webhook call, this field is set to the value of the ``payload`` field returned in the webhook response. - output_contexts (Sequence[~.context.Context]): + output_contexts (Sequence[google.cloud.dialogflow_v2beta1.types.Context]): The collection of output contexts. If applicable, ``output_contexts.parameters`` contains entries with name ``.original`` containing the original parameter values before the query. - intent (~.gcd_intent.Intent): + intent (google.cloud.dialogflow_v2beta1.types.Intent): The intent that matched the conversational query. Some, not all fields are filled in this message, including but not limited to: ``name``, ``display_name``, ``end_interaction`` @@ -405,7 +407,7 @@ class QueryResult(proto.Message): ``multiple knowledge_answers`` messages, this value is set to the greatest ``knowledgeAnswers.match_confidence`` value in the list. - diagnostic_info (~.struct.Struct): + diagnostic_info (google.protobuf.struct_pb2.Struct): Free-form diagnostic information for the associated detect intent request. The fields of this data can change without notice, so you @@ -415,11 +417,11 @@ class QueryResult(proto.Message): - webhook call latency - webhook errors - sentiment_analysis_result (~.gcd_session.SentimentAnalysisResult): + sentiment_analysis_result (google.cloud.dialogflow_v2beta1.types.SentimentAnalysisResult): The sentiment analysis result, which depends on the ``sentiment_analysis_request_config`` specified in the request. - knowledge_answers (~.gcd_session.KnowledgeAnswers): + knowledge_answers (google.cloud.dialogflow_v2beta1.types.KnowledgeAnswers): The result from Knowledge Connector (if any), ordered by decreasing ``KnowledgeAnswers.match_confidence``. """ @@ -469,7 +471,7 @@ class KnowledgeAnswers(proto.Message): r"""Represents the result of querying a Knowledge base. Attributes: - answers (Sequence[~.gcd_session.KnowledgeAnswers.Answer]): + answers (Sequence[google.cloud.dialogflow_v2beta1.types.KnowledgeAnswers.Answer]): A list of answers from Knowledge Connector. """ @@ -488,7 +490,7 @@ class Answer(proto.Message): answer (str): The piece of text from the ``source`` knowledge base document that answers this conversational query. - match_confidence_level (~.gcd_session.KnowledgeAnswers.Answer.MatchConfidenceLevel): + match_confidence_level (google.cloud.dialogflow_v2beta1.types.KnowledgeAnswers.Answer.MatchConfidenceLevel): The system's confidence level that this knowledge answer is a good match for this conversational query. NOTE: The confidence level for a given ```` pair may @@ -596,9 +598,9 @@ class StreamingDetectIntentRequest(proto.Message): Note: Always use agent versions for production traffic. See `Versions and environments `__. - query_params (~.gcd_session.QueryParameters): + query_params (google.cloud.dialogflow_v2beta1.types.QueryParameters): The parameters of this query. - query_input (~.gcd_session.QueryInput): + query_input (google.cloud.dialogflow_v2beta1.types.QueryInput): Required. The input specification. It can be set to: 1. an audio config which instructs the speech @@ -619,12 +621,12 @@ class StreamingDetectIntentRequest(proto.Message): is received, the client should close the stream and start a new request with a new stream as needed. This setting is ignored when ``query_input`` is a piece of text or an event. - output_audio_config (~.gcd_audio_config.OutputAudioConfig): + output_audio_config (google.cloud.dialogflow_v2beta1.types.OutputAudioConfig): Instructs the speech synthesizer how to generate the output audio. If this field is not set and agent-level speech synthesizer is not configured, no output audio is generated. - output_audio_config_mask (~.field_mask.FieldMask): + output_audio_config_mask (google.protobuf.field_mask_pb2.FieldMask): Mask for [output_audio_config][google.cloud.dialogflow.v2beta1.StreamingDetectIntentRequest.output_audio_config] indicating which settings in this request-level config @@ -685,13 +687,13 @@ class StreamingDetectIntentResponse(proto.Message): The unique identifier of the response. It can be used to locate a response in the training example set or for reporting issues. - recognition_result (~.gcd_session.StreamingRecognitionResult): + recognition_result (google.cloud.dialogflow_v2beta1.types.StreamingRecognitionResult): The result of speech recognition. - query_result (~.gcd_session.QueryResult): + query_result (google.cloud.dialogflow_v2beta1.types.QueryResult): The selected results of the conversational query or event processing. See ``alternative_query_results`` for additional potential results. - alternative_query_results (Sequence[~.gcd_session.QueryResult]): + alternative_query_results (Sequence[google.cloud.dialogflow_v2beta1.types.QueryResult]): If Knowledge Connectors are enabled, there could be more than one result returned for a given query or event, and this field will contain all results except for the top one, @@ -701,7 +703,7 @@ class StreamingDetectIntentResponse(proto.Message): Connectors are disabled, this field will be empty until multiple responses for regular intents are supported, at which point those additional results will be surfaced here. - webhook_status (~.status.Status): + webhook_status (google.rpc.status_pb2.Status): Specifies the status of the webhook request. output_audio (bytes): The audio data bytes encoded as specified in the request. @@ -715,7 +717,7 @@ class StreamingDetectIntentResponse(proto.Message): 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 (~.gcd_audio_config.OutputAudioConfig): + output_audio_config (google.cloud.dialogflow_v2beta1.types.OutputAudioConfig): The config used by the speech synthesizer to generate the output audio. """ @@ -775,7 +777,7 @@ class StreamingRecognitionResult(proto.Message): - for ``END_OF_SINGLE_UTTERANCE``: only ``message_type``. Attributes: - message_type (~.gcd_session.StreamingRecognitionResult.MessageType): + message_type (google.cloud.dialogflow_v2beta1.types.StreamingRecognitionResult.MessageType): Type of the result message. transcript (str): Transcript text representing the words that the user spoke. @@ -807,17 +809,17 @@ class StreamingRecognitionResult(proto.Message): ``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[~.gcd_audio_config.SpeechWordInfo]): + speech_word_info (Sequence[google.cloud.dialogflow_v2beta1.types.SpeechWordInfo]): Word-specific information for the words recognized by Speech in [transcript][google.cloud.dialogflow.v2beta1.StreamingRecognitionResult.transcript]. Populated if and only if ``message_type`` = ``TRANSCRIPT`` and [InputAudioConfig.enable_word_info] is set. - speech_end_offset (~.duration.Duration): + speech_end_offset (google.protobuf.duration_pb2.Duration): Time offset of the end of this Speech recognition result relative to the beginning of the audio. Only populated for ``message_type`` = ``TRANSCRIPT``. - dtmf_digits (~.gcd_audio_config.TelephonyDtmfEvents): + dtmf_digits (google.cloud.dialogflow_v2beta1.types.TelephonyDtmfEvents): DTMF digits. Populated if and only if ``message_type`` = ``DTMF_DIGITS``. """ @@ -882,7 +884,7 @@ class EventInput(proto.Message): Attributes: name (str): Required. The unique identifier of the event. - parameters (~.struct.Struct): + parameters (google.protobuf.struct_pb2.Struct): The collection of parameters associated with the event. Depending on your protocol or client library @@ -894,9 +896,10 @@ class EventInput(proto.Message): - 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: + composite entity: map - Else: depending on + parameter value type, could be one of string, + number, boolean, null, list or map + - MapValue value: - If parameter's entity type is a composite entity: map from composite entity property names to property values - @@ -946,7 +949,7 @@ class SentimentAnalysisResult(proto.Message): [ConversationProfile.human_agent_assistant_config][google.cloud.dialogflow.v2beta1.ConversationProfile.human_agent_assistant_config] Attributes: - query_text_sentiment (~.gcd_session.Sentiment): + query_text_sentiment (google.cloud.dialogflow_v2beta1.types.Sentiment): The sentiment analysis result for ``query_text``. """ diff --git a/google/cloud/dialogflow_v2beta1/types/session_entity_type.py b/google/cloud/dialogflow_v2beta1/types/session_entity_type.py index 35f61c1ce..2cf83c8da 100644 --- a/google/cloud/dialogflow_v2beta1/types/session_entity_type.py +++ b/google/cloud/dialogflow_v2beta1/types/session_entity_type.py @@ -64,11 +64,11 @@ class SessionEntityType(proto.Message): ```` must be the display name of an existing entity type in the same agent that will be overridden or supplemented. - entity_override_mode (~.gcd_session_entity_type.SessionEntityType.EntityOverrideMode): + entity_override_mode (google.cloud.dialogflow_v2beta1.types.SessionEntityType.EntityOverrideMode): Required. Indicates whether the additional data should override or supplement the custom entity type definition. - entities (Sequence[~.entity_type.EntityType.Entity]): + entities (Sequence[google.cloud.dialogflow_v2beta1.types.EntityType.Entity]): Required. The collection of entities associated with this session entity type. """ @@ -127,7 +127,7 @@ class ListSessionEntityTypesResponse(proto.Message): [SessionEntityTypes.ListSessionEntityTypes][google.cloud.dialogflow.v2beta1.SessionEntityTypes.ListSessionEntityTypes]. Attributes: - session_entity_types (Sequence[~.gcd_session_entity_type.SessionEntityType]): + session_entity_types (Sequence[google.cloud.dialogflow_v2beta1.types.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. @@ -189,7 +189,7 @@ class CreateSessionEntityTypeRequest(proto.Message): location. If ``Environment ID`` is not specified, we assume default 'draft' environment. If ``User ID`` is not specified, we assume default '-' user. - session_entity_type (~.gcd_session_entity_type.SessionEntityType): + session_entity_type (google.cloud.dialogflow_v2beta1.types.SessionEntityType): Required. The session entity type to create. """ @@ -205,9 +205,9 @@ class UpdateSessionEntityTypeRequest(proto.Message): [SessionEntityTypes.UpdateSessionEntityType][google.cloud.dialogflow.v2beta1.SessionEntityTypes.UpdateSessionEntityType]. Attributes: - session_entity_type (~.gcd_session_entity_type.SessionEntityType): + session_entity_type (google.cloud.dialogflow_v2beta1.types.SessionEntityType): Required. The session entity type to update. - update_mask (~.field_mask.FieldMask): + update_mask (google.protobuf.field_mask_pb2.FieldMask): Optional. The mask to control which fields get updated. """ diff --git a/google/cloud/dialogflow_v2beta1/types/validation_result.py b/google/cloud/dialogflow_v2beta1/types/validation_result.py index 097ca3d7e..1371ba1ca 100644 --- a/google/cloud/dialogflow_v2beta1/types/validation_result.py +++ b/google/cloud/dialogflow_v2beta1/types/validation_result.py @@ -28,7 +28,7 @@ class ValidationError(proto.Message): r"""Represents a single validation error. Attributes: - severity (~.validation_result.ValidationError.Severity): + severity (google.cloud.dialogflow_v2beta1.types.ValidationError.Severity): The severity of the error. entries (Sequence[str]): The names of the entries that the error is @@ -73,7 +73,7 @@ class ValidationResult(proto.Message): r"""Represents the output of agent validation. Attributes: - validation_errors (Sequence[~.validation_result.ValidationError]): + validation_errors (Sequence[google.cloud.dialogflow_v2beta1.types.ValidationError]): Contains all validation errors. """ diff --git a/google/cloud/dialogflow_v2beta1/types/webhook.py b/google/cloud/dialogflow_v2beta1/types/webhook.py index 8bd55e799..05ea8848d 100644 --- a/google/cloud/dialogflow_v2beta1/types/webhook.py +++ b/google/cloud/dialogflow_v2beta1/types/webhook.py @@ -47,14 +47,14 @@ class WebhookRequest(proto.Message): response_id (str): The unique identifier of the response. Contains the same value as ``[Streaming]DetectIntentResponse.response_id``. - query_result (~.gcd_session.QueryResult): + query_result (google.cloud.dialogflow_v2beta1.types.QueryResult): The result of the conversational query or event processing. Contains the same value as ``[Streaming]DetectIntentResponse.query_result``. - alternative_query_results (Sequence[~.gcd_session.QueryResult]): + alternative_query_results (Sequence[google.cloud.dialogflow_v2beta1.types.QueryResult]): Alternative query results from KnowledgeService. - original_detect_intent_request (~.webhook.OriginalDetectIntentRequest): + original_detect_intent_request (google.cloud.dialogflow_v2beta1.types.OriginalDetectIntentRequest): Optional. The contents of the original request that was passed to ``[Streaming]DetectIntent`` call. """ @@ -100,7 +100,7 @@ class WebhookResponse(proto.Message): provided, Dialogflow uses this field to populate [QueryResult.fulfillment_text][google.cloud.dialogflow.v2beta1.QueryResult.fulfillment_text] sent to the integration or API caller. - fulfillment_messages (Sequence[~.intent.Intent.Message]): + fulfillment_messages (Sequence[google.cloud.dialogflow_v2beta1.types.Intent.Message]): Optional. The rich response messages intended for the end-user. When provided, Dialogflow uses this field to populate @@ -112,7 +112,7 @@ class WebhookResponse(proto.Message): Dialogflow uses this field to populate [QueryResult.webhook_source][google.cloud.dialogflow.v2beta1.QueryResult.webhook_source] sent to the integration or API caller. - payload (~.struct.Struct): + payload (google.protobuf.struct_pb2.Struct): Optional. This field can be used to pass custom data from your webhook to the integration or API caller. Arbitrary JSON objects are supported. When provided, Dialogflow uses @@ -124,24 +124,29 @@ class WebhookResponse(proto.Message): for rich response messages. See the format definition at `Google Assistant Dialogflow webhook format `__ - output_contexts (Sequence[~.context.Context]): + output_contexts (Sequence[google.cloud.dialogflow_v2beta1.types.Context]): Optional. The collection of output contexts that will overwrite currently active contexts for the session and reset their lifespans. When provided, Dialogflow uses this field to populate [QueryResult.output_contexts][google.cloud.dialogflow.v2beta1.QueryResult.output_contexts] sent to the integration or API caller. - followup_event_input (~.gcd_session.EventInput): + followup_event_input (google.cloud.dialogflow_v2beta1.types.EventInput): Optional. Invokes the supplied events. When this field is set, Dialogflow ignores the ``fulfillment_text``, ``fulfillment_messages``, and ``payload`` fields. + live_agent_handoff (bool): + Indicates that a live agent should be brought in to handle + the interaction with the user. In most cases, when you set + this flag to true, you would also want to set + end_interaction to true as well. Default is false. end_interaction (bool): Optional. Indicates that this intent ends an interaction. Some integrations (e.g., Actions on Google or Dialogflow phone gateway) use this information to close interaction with an end user. Default is false. - session_entity_types (Sequence[~.session_entity_type.SessionEntityType]): + session_entity_types (Sequence[google.cloud.dialogflow_v2beta1.types.SessionEntityType]): Optional. Additional session entity types to replace or extend developer entity types with. The entity synonyms apply to all languages and persist for the session. Setting @@ -170,6 +175,8 @@ class WebhookResponse(proto.Message): proto.MESSAGE, number=6, message=gcd_session.EventInput, ) + live_agent_handoff = proto.Field(proto.BOOL, number=7) + end_interaction = proto.Field(proto.BOOL, number=8) session_entity_types = proto.RepeatedField( @@ -188,7 +195,7 @@ class OriginalDetectIntentRequest(proto.Message): version (str): Optional. The version of the protocol used for this request. This field is AoG-specific. - payload (~.struct.Struct): + payload (google.protobuf.struct_pb2.Struct): Optional. This field is set to the value of the ``QueryParameters.payload`` field passed in the request. Some integrations that query a Dialogflow agent may provide diff --git a/noxfile.py b/noxfile.py index 7c06093dd..0ad24c69c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -170,7 +170,7 @@ def cover(session): test runs (not system test runs), and then erases coverage data. """ session.install("coverage", "pytest-cov") - session.run("coverage", "report", "--show-missing", "--fail-under=100") + session.run("coverage", "report", "--show-missing", "--fail-under=99") session.run("coverage", "erase") diff --git a/scripts/fixup_dialogflow_v2_keywords.py b/scripts/fixup_dialogflow_v2_keywords.py index bb125c8fb..3866fb44a 100644 --- a/scripts/fixup_dialogflow_v2_keywords.py +++ b/scripts/fixup_dialogflow_v2_keywords.py @@ -41,6 +41,7 @@ def partition( class dialogflowCallTransformer(cst.CSTTransformer): CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { + 'analyze_content': ('participant', 'text_input', 'audio_input', 'event_input', 'reply_audio_config', 'query_params', 'request_id', ), 'batch_create_entities': ('parent', 'entities', 'language_code', ), 'batch_delete_entities': ('parent', 'entity_values', 'language_code', ), 'batch_delete_entity_types': ('parent', 'entity_type_names', ), @@ -48,38 +49,71 @@ class dialogflowCallTransformer(cst.CSTTransformer): 'batch_update_entities': ('parent', 'entities', 'language_code', 'update_mask', ), 'batch_update_entity_types': ('parent', 'entity_type_batch_uri', 'entity_type_batch_inline', 'language_code', 'update_mask', ), 'batch_update_intents': ('parent', 'intent_batch_uri', 'intent_batch_inline', 'language_code', 'update_mask', 'intent_view', ), + 'complete_conversation': ('name', ), + 'create_call_matcher': ('parent', 'call_matcher', ), 'create_context': ('parent', 'context', ), + 'create_conversation': ('parent', 'conversation', 'conversation_id', ), + 'create_conversation_profile': ('parent', 'conversation_profile', ), + 'create_document': ('parent', 'document', ), 'create_entity_type': ('parent', 'entity_type', 'language_code', ), 'create_intent': ('parent', 'intent', 'language_code', 'intent_view', ), + 'create_knowledge_base': ('parent', 'knowledge_base', ), + 'create_participant': ('parent', 'participant', ), 'create_session_entity_type': ('parent', 'session_entity_type', ), 'delete_agent': ('parent', ), 'delete_all_contexts': ('parent', ), + 'delete_call_matcher': ('name', ), 'delete_context': ('name', ), + 'delete_conversation_profile': ('name', ), + 'delete_document': ('name', ), 'delete_entity_type': ('name', ), 'delete_intent': ('name', ), + 'delete_knowledge_base': ('name', 'force', ), 'delete_session_entity_type': ('name', ), 'detect_intent': ('session', 'query_input', 'query_params', 'output_audio_config', 'output_audio_config_mask', 'input_audio', ), 'export_agent': ('parent', 'agent_uri', ), 'get_agent': ('parent', ), 'get_context': ('name', ), + 'get_conversation': ('name', ), + 'get_conversation_profile': ('name', ), + 'get_document': ('name', ), 'get_entity_type': ('name', 'language_code', ), 'get_intent': ('name', 'language_code', 'intent_view', ), + 'get_knowledge_base': ('name', ), + 'get_participant': ('name', ), 'get_session_entity_type': ('name', ), 'get_validation_result': ('parent', 'language_code', ), 'import_agent': ('parent', 'agent_uri', 'agent_content', ), + 'list_answer_records': ('parent', 'filter', 'page_size', 'page_token', ), + 'list_call_matchers': ('parent', 'page_size', 'page_token', ), 'list_contexts': ('parent', 'page_size', 'page_token', ), + 'list_conversation_profiles': ('parent', 'page_size', 'page_token', ), + 'list_conversations': ('parent', 'page_size', 'page_token', 'filter', ), + 'list_documents': ('parent', 'page_size', 'page_token', ), 'list_entity_types': ('parent', 'language_code', 'page_size', 'page_token', ), 'list_environments': ('parent', 'page_size', 'page_token', ), 'list_intents': ('parent', 'language_code', 'intent_view', 'page_size', 'page_token', ), + 'list_knowledge_bases': ('parent', 'page_size', 'page_token', ), + 'list_messages': ('parent', 'filter', 'page_size', 'page_token', ), + 'list_participants': ('parent', 'page_size', 'page_token', ), 'list_session_entity_types': ('parent', 'page_size', 'page_token', ), + 'reload_document': ('name', 'content_uri', ), 'restore_agent': ('parent', 'agent_uri', 'agent_content', ), 'search_agents': ('parent', 'page_size', 'page_token', ), 'set_agent': ('agent', 'update_mask', ), + 'streaming_analyze_content': ('participant', 'audio_config', 'text_config', 'reply_audio_config', 'input_audio', 'input_text', 'input_dtmf', 'query_params', ), 'streaming_detect_intent': ('session', 'query_input', 'query_params', 'single_utterance', 'output_audio_config', 'output_audio_config_mask', 'input_audio', ), + 'suggest_articles': ('parent', 'latest_message', 'context_size', ), + 'suggest_faq_answers': ('parent', 'latest_message', 'context_size', ), 'train_agent': ('parent', ), + 'update_answer_record': ('answer_record', 'update_mask', ), 'update_context': ('context', 'update_mask', ), + 'update_conversation_profile': ('conversation_profile', 'update_mask', ), + 'update_document': ('document', 'update_mask', ), 'update_entity_type': ('entity_type', 'language_code', 'update_mask', ), 'update_intent': ('intent', 'language_code', 'update_mask', 'intent_view', ), + 'update_knowledge_base': ('knowledge_base', 'update_mask', ), + 'update_participant': ('participant', 'update_mask', ), 'update_session_entity_type': ('session_entity_type', 'update_mask', ), } diff --git a/scripts/fixup_dialogflow_v2beta1_keywords.py b/scripts/fixup_dialogflow_v2beta1_keywords.py index 00f5486b2..7d019940b 100644 --- a/scripts/fixup_dialogflow_v2beta1_keywords.py +++ b/scripts/fixup_dialogflow_v2beta1_keywords.py @@ -41,22 +41,32 @@ def partition( class dialogflowCallTransformer(cst.CSTTransformer): CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { + 'analyze_content': ('participant', 'text', 'audio', 'text_input', 'audio_input', 'event_input', 'reply_audio_config', 'query_params', 'message_send_time', 'request_id', ), 'batch_create_entities': ('parent', 'entities', 'language_code', ), + 'batch_create_messages': ('parent', 'requests', ), 'batch_delete_entities': ('parent', 'entity_values', 'language_code', ), 'batch_delete_entity_types': ('parent', 'entity_type_names', ), 'batch_delete_intents': ('parent', 'intents', ), 'batch_update_entities': ('parent', 'entities', 'language_code', 'update_mask', ), 'batch_update_entity_types': ('parent', 'entity_type_batch_uri', 'entity_type_batch_inline', 'language_code', 'update_mask', ), 'batch_update_intents': ('parent', 'intent_batch_uri', 'intent_batch_inline', 'language_code', 'update_mask', 'intent_view', ), + 'compile_suggestion': ('parent', 'latest_message', 'context_size', ), + 'complete_conversation': ('name', ), + 'create_call_matcher': ('parent', 'call_matcher', ), 'create_context': ('parent', 'context', ), + 'create_conversation': ('parent', 'conversation', 'conversation_id', ), + 'create_conversation_profile': ('parent', 'conversation_profile', ), 'create_document': ('parent', 'document', 'import_gcs_custom_metadata', ), 'create_entity_type': ('parent', 'entity_type', 'language_code', ), 'create_intent': ('parent', 'intent', 'language_code', 'intent_view', ), 'create_knowledge_base': ('parent', 'knowledge_base', ), + 'create_participant': ('parent', 'participant', ), 'create_session_entity_type': ('parent', 'session_entity_type', ), 'delete_agent': ('parent', ), 'delete_all_contexts': ('parent', ), + 'delete_call_matcher': ('name', ), 'delete_context': ('name', ), + 'delete_conversation_profile': ('name', ), 'delete_document': ('name', ), 'delete_entity_type': ('name', ), 'delete_intent': ('name', ), @@ -65,32 +75,51 @@ class dialogflowCallTransformer(cst.CSTTransformer): 'detect_intent': ('session', 'query_input', 'query_params', 'output_audio_config', 'output_audio_config_mask', 'input_audio', ), 'export_agent': ('parent', 'agent_uri', ), 'get_agent': ('parent', ), + 'get_answer_record': ('name', ), 'get_context': ('name', ), + 'get_conversation': ('name', ), + 'get_conversation_profile': ('name', ), 'get_document': ('name', ), 'get_entity_type': ('name', 'language_code', ), 'get_intent': ('name', 'language_code', 'intent_view', ), 'get_knowledge_base': ('name', ), + 'get_participant': ('name', ), 'get_session_entity_type': ('name', ), 'get_validation_result': ('parent', 'language_code', ), 'import_agent': ('parent', 'agent_uri', 'agent_content', ), + 'import_documents': ('parent', 'document_template', 'gcs_source', 'import_gcs_custom_metadata', ), + 'list_answer_records': ('parent', 'page_size', 'page_token', ), + 'list_call_matchers': ('parent', 'page_size', 'page_token', ), 'list_contexts': ('parent', 'page_size', 'page_token', ), + 'list_conversation_profiles': ('parent', 'page_size', 'page_token', ), + 'list_conversations': ('parent', 'page_size', 'page_token', 'filter', ), 'list_documents': ('parent', 'page_size', 'page_token', 'filter', ), 'list_entity_types': ('parent', 'language_code', 'page_size', 'page_token', ), 'list_environments': ('parent', 'page_size', 'page_token', ), 'list_intents': ('parent', 'language_code', 'intent_view', 'page_size', 'page_token', ), 'list_knowledge_bases': ('parent', 'page_size', 'page_token', 'filter', ), + 'list_messages': ('parent', 'filter', 'page_size', 'page_token', ), + 'list_participants': ('parent', 'page_size', 'page_token', ), 'list_session_entity_types': ('parent', 'page_size', 'page_token', ), + 'list_suggestions': ('parent', 'page_size', 'page_token', 'filter', ), 'reload_document': ('name', 'gcs_source', 'import_gcs_custom_metadata', ), 'restore_agent': ('parent', 'agent_uri', 'agent_content', ), 'search_agents': ('parent', 'page_size', 'page_token', ), 'set_agent': ('agent', 'update_mask', ), + 'streaming_analyze_content': ('participant', 'audio_config', 'text_config', 'reply_audio_config', 'input_audio', 'input_text', 'input_dtmf', 'query_params', 'enable_extended_streaming', ), 'streaming_detect_intent': ('session', 'query_input', 'query_params', 'single_utterance', 'output_audio_config', 'output_audio_config_mask', 'input_audio', ), + 'suggest_articles': ('parent', 'latest_message', 'context_size', ), + 'suggest_faq_answers': ('parent', 'latest_message', 'context_size', ), + 'suggest_smart_replies': ('parent', 'current_text_input', 'latest_message', 'context_size', ), 'train_agent': ('parent', ), + 'update_answer_record': ('answer_record', 'update_mask', ), 'update_context': ('context', 'update_mask', ), + 'update_conversation_profile': ('conversation_profile', 'update_mask', ), 'update_document': ('document', 'update_mask', ), 'update_entity_type': ('entity_type', 'language_code', 'update_mask', ), 'update_intent': ('intent', 'language_code', 'update_mask', 'intent_view', ), 'update_knowledge_base': ('knowledge_base', 'update_mask', ), + 'update_participant': ('participant', 'update_mask', ), 'update_session_entity_type': ('session_entity_type', 'update_mask', ), } diff --git a/synth.metadata b/synth.metadata index f65433764..db81540e2 100644 --- a/synth.metadata +++ b/synth.metadata @@ -3,30 +3,30 @@ { "git": { "name": ".", - "remote": "https://github.com/googleapis/python-dialogflow.git", - "sha": "a2b7db9d27d20c09682e9d916b85bb95d1c3a3e4" + "remote": "git@github.com:googleapis/python-dialogflow", + "sha": "85e86bb0b9c03497a52c08990a763d6a665f6c40" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "6dae98144d466d4f985b926baec6208b01572f55", - "internalRef": "347459563" + "sha": "4b16c60a8fffe213d3a5002f85696fef2b6a8172", + "internalRef": "362090097" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "0780323da96d5a53925fe0547757181fe76e8f1e" + "sha": "ac8f20f12e7a4c0b0ae1c6fa415f684a25ea82b7" } }, { "git": { "name": "synthtool", "remote": "https://github.com/googleapis/synthtool.git", - "sha": "0780323da96d5a53925fe0547757181fe76e8f1e" + "sha": "ac8f20f12e7a4c0b0ae1c6fa415f684a25ea82b7" } } ], @@ -49,281 +49,5 @@ "generator": "bazel" } } - ], - "generatedFiles": [ - ".flake8", - ".github/CONTRIBUTING.md", - ".github/ISSUE_TEMPLATE/bug_report.md", - ".github/ISSUE_TEMPLATE/feature_request.md", - ".github/ISSUE_TEMPLATE/support_request.md", - ".github/PULL_REQUEST_TEMPLATE.md", - ".github/header-checker-lint.yml", - ".github/release-please.yml", - ".github/snippet-bot.yml", - ".gitignore", - ".kokoro/build.sh", - ".kokoro/continuous/common.cfg", - ".kokoro/continuous/continuous.cfg", - ".kokoro/docker/docs/Dockerfile", - ".kokoro/docker/docs/fetch_gpg_keys.sh", - ".kokoro/docs/common.cfg", - ".kokoro/docs/docs-presubmit.cfg", - ".kokoro/docs/docs.cfg", - ".kokoro/populate-secrets.sh", - ".kokoro/presubmit/common.cfg", - ".kokoro/presubmit/presubmit.cfg", - ".kokoro/publish-docs.sh", - ".kokoro/release.sh", - ".kokoro/release/common.cfg", - ".kokoro/release/release.cfg", - ".kokoro/samples/lint/common.cfg", - ".kokoro/samples/lint/continuous.cfg", - ".kokoro/samples/lint/periodic.cfg", - ".kokoro/samples/lint/presubmit.cfg", - ".kokoro/samples/python3.6/common.cfg", - ".kokoro/samples/python3.6/continuous.cfg", - ".kokoro/samples/python3.6/periodic.cfg", - ".kokoro/samples/python3.6/presubmit.cfg", - ".kokoro/samples/python3.7/common.cfg", - ".kokoro/samples/python3.7/continuous.cfg", - ".kokoro/samples/python3.7/periodic.cfg", - ".kokoro/samples/python3.7/presubmit.cfg", - ".kokoro/samples/python3.8/common.cfg", - ".kokoro/samples/python3.8/continuous.cfg", - ".kokoro/samples/python3.8/periodic.cfg", - ".kokoro/samples/python3.8/presubmit.cfg", - ".kokoro/test-samples.sh", - ".kokoro/trampoline.sh", - ".kokoro/trampoline_v2.sh", - ".pre-commit-config.yaml", - ".trampolinerc", - "CODE_OF_CONDUCT.md", - "CONTRIBUTING.rst", - "LICENSE", - "MANIFEST.in", - "docs/_static/custom.css", - "docs/_templates/layout.html", - "docs/conf.py", - "docs/dialogflow_v2/services.rst", - "docs/dialogflow_v2/types.rst", - "docs/dialogflow_v2beta1/services.rst", - "docs/dialogflow_v2beta1/types.rst", - "docs/multiprocessing.rst", - "google/cloud/dialogflow/__init__.py", - "google/cloud/dialogflow/py.typed", - "google/cloud/dialogflow_v2/__init__.py", - "google/cloud/dialogflow_v2/proto/agent.proto", - "google/cloud/dialogflow_v2/proto/audio_config.proto", - "google/cloud/dialogflow_v2/proto/context.proto", - "google/cloud/dialogflow_v2/proto/entity_type.proto", - "google/cloud/dialogflow_v2/proto/environment.proto", - "google/cloud/dialogflow_v2/proto/intent.proto", - "google/cloud/dialogflow_v2/proto/session.proto", - "google/cloud/dialogflow_v2/proto/session_entity_type.proto", - "google/cloud/dialogflow_v2/proto/validation_result.proto", - "google/cloud/dialogflow_v2/proto/webhook.proto", - "google/cloud/dialogflow_v2/py.typed", - "google/cloud/dialogflow_v2/services/__init__.py", - "google/cloud/dialogflow_v2/services/agents/__init__.py", - "google/cloud/dialogflow_v2/services/agents/async_client.py", - "google/cloud/dialogflow_v2/services/agents/client.py", - "google/cloud/dialogflow_v2/services/agents/pagers.py", - "google/cloud/dialogflow_v2/services/agents/transports/__init__.py", - "google/cloud/dialogflow_v2/services/agents/transports/base.py", - "google/cloud/dialogflow_v2/services/agents/transports/grpc.py", - "google/cloud/dialogflow_v2/services/agents/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2/services/contexts/__init__.py", - "google/cloud/dialogflow_v2/services/contexts/async_client.py", - "google/cloud/dialogflow_v2/services/contexts/client.py", - "google/cloud/dialogflow_v2/services/contexts/pagers.py", - "google/cloud/dialogflow_v2/services/contexts/transports/__init__.py", - "google/cloud/dialogflow_v2/services/contexts/transports/base.py", - "google/cloud/dialogflow_v2/services/contexts/transports/grpc.py", - "google/cloud/dialogflow_v2/services/contexts/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2/services/entity_types/__init__.py", - "google/cloud/dialogflow_v2/services/entity_types/async_client.py", - "google/cloud/dialogflow_v2/services/entity_types/client.py", - "google/cloud/dialogflow_v2/services/entity_types/pagers.py", - "google/cloud/dialogflow_v2/services/entity_types/transports/__init__.py", - "google/cloud/dialogflow_v2/services/entity_types/transports/base.py", - "google/cloud/dialogflow_v2/services/entity_types/transports/grpc.py", - "google/cloud/dialogflow_v2/services/entity_types/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2/services/environments/__init__.py", - "google/cloud/dialogflow_v2/services/environments/async_client.py", - "google/cloud/dialogflow_v2/services/environments/client.py", - "google/cloud/dialogflow_v2/services/environments/pagers.py", - "google/cloud/dialogflow_v2/services/environments/transports/__init__.py", - "google/cloud/dialogflow_v2/services/environments/transports/base.py", - "google/cloud/dialogflow_v2/services/environments/transports/grpc.py", - "google/cloud/dialogflow_v2/services/environments/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2/services/intents/__init__.py", - "google/cloud/dialogflow_v2/services/intents/async_client.py", - "google/cloud/dialogflow_v2/services/intents/client.py", - "google/cloud/dialogflow_v2/services/intents/pagers.py", - "google/cloud/dialogflow_v2/services/intents/transports/__init__.py", - "google/cloud/dialogflow_v2/services/intents/transports/base.py", - "google/cloud/dialogflow_v2/services/intents/transports/grpc.py", - "google/cloud/dialogflow_v2/services/intents/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2/services/session_entity_types/__init__.py", - "google/cloud/dialogflow_v2/services/session_entity_types/async_client.py", - "google/cloud/dialogflow_v2/services/session_entity_types/client.py", - "google/cloud/dialogflow_v2/services/session_entity_types/pagers.py", - "google/cloud/dialogflow_v2/services/session_entity_types/transports/__init__.py", - "google/cloud/dialogflow_v2/services/session_entity_types/transports/base.py", - "google/cloud/dialogflow_v2/services/session_entity_types/transports/grpc.py", - "google/cloud/dialogflow_v2/services/session_entity_types/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2/services/sessions/__init__.py", - "google/cloud/dialogflow_v2/services/sessions/async_client.py", - "google/cloud/dialogflow_v2/services/sessions/client.py", - "google/cloud/dialogflow_v2/services/sessions/transports/__init__.py", - "google/cloud/dialogflow_v2/services/sessions/transports/base.py", - "google/cloud/dialogflow_v2/services/sessions/transports/grpc.py", - "google/cloud/dialogflow_v2/services/sessions/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2/types/__init__.py", - "google/cloud/dialogflow_v2/types/agent.py", - "google/cloud/dialogflow_v2/types/audio_config.py", - "google/cloud/dialogflow_v2/types/context.py", - "google/cloud/dialogflow_v2/types/entity_type.py", - "google/cloud/dialogflow_v2/types/environment.py", - "google/cloud/dialogflow_v2/types/intent.py", - "google/cloud/dialogflow_v2/types/session.py", - "google/cloud/dialogflow_v2/types/session_entity_type.py", - "google/cloud/dialogflow_v2/types/validation_result.py", - "google/cloud/dialogflow_v2/types/webhook.py", - "google/cloud/dialogflow_v2beta1/__init__.py", - "google/cloud/dialogflow_v2beta1/proto/agent.proto", - "google/cloud/dialogflow_v2beta1/proto/audio_config.proto", - "google/cloud/dialogflow_v2beta1/proto/context.proto", - "google/cloud/dialogflow_v2beta1/proto/document.proto", - "google/cloud/dialogflow_v2beta1/proto/entity_type.proto", - "google/cloud/dialogflow_v2beta1/proto/environment.proto", - "google/cloud/dialogflow_v2beta1/proto/gcs.proto", - "google/cloud/dialogflow_v2beta1/proto/intent.proto", - "google/cloud/dialogflow_v2beta1/proto/knowledge_base.proto", - "google/cloud/dialogflow_v2beta1/proto/session.proto", - "google/cloud/dialogflow_v2beta1/proto/session_entity_type.proto", - "google/cloud/dialogflow_v2beta1/proto/validation_result.proto", - "google/cloud/dialogflow_v2beta1/proto/webhook.proto", - "google/cloud/dialogflow_v2beta1/py.typed", - "google/cloud/dialogflow_v2beta1/services/__init__.py", - "google/cloud/dialogflow_v2beta1/services/agents/__init__.py", - "google/cloud/dialogflow_v2beta1/services/agents/async_client.py", - "google/cloud/dialogflow_v2beta1/services/agents/client.py", - "google/cloud/dialogflow_v2beta1/services/agents/pagers.py", - "google/cloud/dialogflow_v2beta1/services/agents/transports/__init__.py", - "google/cloud/dialogflow_v2beta1/services/agents/transports/base.py", - "google/cloud/dialogflow_v2beta1/services/agents/transports/grpc.py", - "google/cloud/dialogflow_v2beta1/services/agents/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2beta1/services/contexts/__init__.py", - "google/cloud/dialogflow_v2beta1/services/contexts/async_client.py", - "google/cloud/dialogflow_v2beta1/services/contexts/client.py", - "google/cloud/dialogflow_v2beta1/services/contexts/pagers.py", - "google/cloud/dialogflow_v2beta1/services/contexts/transports/__init__.py", - "google/cloud/dialogflow_v2beta1/services/contexts/transports/base.py", - "google/cloud/dialogflow_v2beta1/services/contexts/transports/grpc.py", - "google/cloud/dialogflow_v2beta1/services/contexts/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2beta1/services/documents/__init__.py", - "google/cloud/dialogflow_v2beta1/services/documents/async_client.py", - "google/cloud/dialogflow_v2beta1/services/documents/client.py", - "google/cloud/dialogflow_v2beta1/services/documents/pagers.py", - "google/cloud/dialogflow_v2beta1/services/documents/transports/__init__.py", - "google/cloud/dialogflow_v2beta1/services/documents/transports/base.py", - "google/cloud/dialogflow_v2beta1/services/documents/transports/grpc.py", - "google/cloud/dialogflow_v2beta1/services/documents/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2beta1/services/entity_types/__init__.py", - "google/cloud/dialogflow_v2beta1/services/entity_types/async_client.py", - "google/cloud/dialogflow_v2beta1/services/entity_types/client.py", - "google/cloud/dialogflow_v2beta1/services/entity_types/pagers.py", - "google/cloud/dialogflow_v2beta1/services/entity_types/transports/__init__.py", - "google/cloud/dialogflow_v2beta1/services/entity_types/transports/base.py", - "google/cloud/dialogflow_v2beta1/services/entity_types/transports/grpc.py", - "google/cloud/dialogflow_v2beta1/services/entity_types/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2beta1/services/environments/__init__.py", - "google/cloud/dialogflow_v2beta1/services/environments/async_client.py", - "google/cloud/dialogflow_v2beta1/services/environments/client.py", - "google/cloud/dialogflow_v2beta1/services/environments/pagers.py", - "google/cloud/dialogflow_v2beta1/services/environments/transports/__init__.py", - "google/cloud/dialogflow_v2beta1/services/environments/transports/base.py", - "google/cloud/dialogflow_v2beta1/services/environments/transports/grpc.py", - "google/cloud/dialogflow_v2beta1/services/environments/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2beta1/services/intents/__init__.py", - "google/cloud/dialogflow_v2beta1/services/intents/async_client.py", - "google/cloud/dialogflow_v2beta1/services/intents/client.py", - "google/cloud/dialogflow_v2beta1/services/intents/pagers.py", - "google/cloud/dialogflow_v2beta1/services/intents/transports/__init__.py", - "google/cloud/dialogflow_v2beta1/services/intents/transports/base.py", - "google/cloud/dialogflow_v2beta1/services/intents/transports/grpc.py", - "google/cloud/dialogflow_v2beta1/services/intents/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2beta1/services/knowledge_bases/__init__.py", - "google/cloud/dialogflow_v2beta1/services/knowledge_bases/async_client.py", - "google/cloud/dialogflow_v2beta1/services/knowledge_bases/client.py", - "google/cloud/dialogflow_v2beta1/services/knowledge_bases/pagers.py", - "google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/__init__.py", - "google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/base.py", - "google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/grpc.py", - "google/cloud/dialogflow_v2beta1/services/knowledge_bases/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2beta1/services/session_entity_types/__init__.py", - "google/cloud/dialogflow_v2beta1/services/session_entity_types/async_client.py", - "google/cloud/dialogflow_v2beta1/services/session_entity_types/client.py", - "google/cloud/dialogflow_v2beta1/services/session_entity_types/pagers.py", - "google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/__init__.py", - "google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/base.py", - "google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/grpc.py", - "google/cloud/dialogflow_v2beta1/services/session_entity_types/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2beta1/services/sessions/__init__.py", - "google/cloud/dialogflow_v2beta1/services/sessions/async_client.py", - "google/cloud/dialogflow_v2beta1/services/sessions/client.py", - "google/cloud/dialogflow_v2beta1/services/sessions/transports/__init__.py", - "google/cloud/dialogflow_v2beta1/services/sessions/transports/base.py", - "google/cloud/dialogflow_v2beta1/services/sessions/transports/grpc.py", - "google/cloud/dialogflow_v2beta1/services/sessions/transports/grpc_asyncio.py", - "google/cloud/dialogflow_v2beta1/types/__init__.py", - "google/cloud/dialogflow_v2beta1/types/agent.py", - "google/cloud/dialogflow_v2beta1/types/audio_config.py", - "google/cloud/dialogflow_v2beta1/types/context.py", - "google/cloud/dialogflow_v2beta1/types/document.py", - "google/cloud/dialogflow_v2beta1/types/entity_type.py", - "google/cloud/dialogflow_v2beta1/types/environment.py", - "google/cloud/dialogflow_v2beta1/types/gcs.py", - "google/cloud/dialogflow_v2beta1/types/intent.py", - "google/cloud/dialogflow_v2beta1/types/knowledge_base.py", - "google/cloud/dialogflow_v2beta1/types/session.py", - "google/cloud/dialogflow_v2beta1/types/session_entity_type.py", - "google/cloud/dialogflow_v2beta1/types/validation_result.py", - "google/cloud/dialogflow_v2beta1/types/webhook.py", - "mypy.ini", - "noxfile.py", - "renovate.json", - "samples/AUTHORING_GUIDE.md", - "samples/CONTRIBUTING.md", - "scripts/decrypt-secrets.sh", - "scripts/fixup_dialogflow_v2_keywords.py", - "scripts/fixup_dialogflow_v2beta1_keywords.py", - "scripts/readme-gen/readme_gen.py", - "scripts/readme-gen/templates/README.tmpl.rst", - "scripts/readme-gen/templates/auth.tmpl.rst", - "scripts/readme-gen/templates/auth_api_key.tmpl.rst", - "scripts/readme-gen/templates/install_deps.tmpl.rst", - "scripts/readme-gen/templates/install_portaudio.tmpl.rst", - "setup.cfg", - "testing/.gitignore", - "tests/unit/gapic/dialogflow_v2/__init__.py", - "tests/unit/gapic/dialogflow_v2/test_agents.py", - "tests/unit/gapic/dialogflow_v2/test_contexts.py", - "tests/unit/gapic/dialogflow_v2/test_entity_types.py", - "tests/unit/gapic/dialogflow_v2/test_environments.py", - "tests/unit/gapic/dialogflow_v2/test_intents.py", - "tests/unit/gapic/dialogflow_v2/test_session_entity_types.py", - "tests/unit/gapic/dialogflow_v2/test_sessions.py", - "tests/unit/gapic/dialogflow_v2beta1/__init__.py", - "tests/unit/gapic/dialogflow_v2beta1/test_agents.py", - "tests/unit/gapic/dialogflow_v2beta1/test_contexts.py", - "tests/unit/gapic/dialogflow_v2beta1/test_documents.py", - "tests/unit/gapic/dialogflow_v2beta1/test_entity_types.py", - "tests/unit/gapic/dialogflow_v2beta1/test_environments.py", - "tests/unit/gapic/dialogflow_v2beta1/test_intents.py", - "tests/unit/gapic/dialogflow_v2beta1/test_knowledge_bases.py", - "tests/unit/gapic/dialogflow_v2beta1/test_session_entity_types.py", - "tests/unit/gapic/dialogflow_v2beta1/test_sessions.py" ] } \ No newline at end of file diff --git a/synth.py b/synth.py index fc33b8192..602691e48 100644 --- a/synth.py +++ b/synth.py @@ -31,7 +31,7 @@ include_protos=True, ) - s.move(library, excludes=["docs/index.rst", "setup.py"]) + s.move(library, excludes=["docs/index.rst", "setup.py", "README.rst"]) # # ---------------------------------------------------------------------------- # # Add templated files @@ -39,6 +39,8 @@ templated_files = common.py_library( samples=False, # set to True only if there are samples microgenerator=True, + cov_level=99, + ) s.move(templated_files, excludes=[".coveragerc"]) # microgenerator has a good .coveragerc file diff --git a/tests/unit/gapic/dialogflow_v2/__init__.py b/tests/unit/gapic/dialogflow_v2/__init__.py index 8b1378917..42ffdf2bc 100644 --- a/tests/unit/gapic/dialogflow_v2/__init__.py +++ b/tests/unit/gapic/dialogflow_v2/__init__.py @@ -1 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/tests/unit/gapic/dialogflow_v2/test_agents.py b/tests/unit/gapic/dialogflow_v2/test_agents.py index 0285e78eb..12e3d6800 100644 --- a/tests/unit/gapic/dialogflow_v2/test_agents.py +++ b/tests/unit/gapic/dialogflow_v2/test_agents.py @@ -86,7 +86,22 @@ def test__get_default_mtls_endpoint(): assert AgentsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [AgentsClient, AgentsAsyncClient]) +@pytest.mark.parametrize("client_class", [AgentsClient, AgentsAsyncClient,]) +def test_agents_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [AgentsClient, AgentsAsyncClient,]) def test_agents_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -95,16 +110,21 @@ def test_agents_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_agents_client_get_transport_class(): transport = AgentsClient.get_transport_class() - assert transport == transports.AgentsGrpcTransport + available_transports = [ + transports.AgentsGrpcTransport, + ] + assert transport in available_transports transport = AgentsClient.get_transport_class("grpc") assert transport == transports.AgentsGrpcTransport @@ -145,7 +165,7 @@ def test_agents_client_client_options(client_class, transport_class, transport_n credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -161,7 +181,7 @@ def test_agents_client_client_options(client_class, transport_class, transport_n credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -177,7 +197,7 @@ def test_agents_client_client_options(client_class, transport_class, transport_n credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -205,7 +225,7 @@ def test_agents_client_client_options(client_class, transport_class, transport_n credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -252,29 +272,25 @@ def test_agents_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -283,66 +299,53 @@ def test_agents_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -364,7 +367,7 @@ def test_agents_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -390,7 +393,7 @@ def test_agents_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -407,7 +410,7 @@ def test_agents_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -481,6 +484,22 @@ def test_get_agent_from_dict(): test_get_agent(request_type=dict) +def test_get_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: + client.get_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.GetAgentRequest() + + @pytest.mark.asyncio async def test_get_agent_async( transport: str = "grpc_asyncio", request_type=agent.GetAgentRequest @@ -736,6 +755,22 @@ def test_set_agent_from_dict(): test_set_agent(request_type=dict) +def test_set_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_agent), "__call__") as call: + client.set_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_agent.SetAgentRequest() + + @pytest.mark.asyncio async def test_set_agent_async( transport: str = "grpc_asyncio", request_type=gcd_agent.SetAgentRequest @@ -957,6 +992,22 @@ def test_delete_agent_from_dict(): test_delete_agent(request_type=dict) +def test_delete_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_agent), "__call__") as call: + client.delete_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.DeleteAgentRequest() + + @pytest.mark.asyncio async def test_delete_agent_async( transport: str = "grpc_asyncio", request_type=agent.DeleteAgentRequest @@ -1140,6 +1191,22 @@ def test_search_agents_from_dict(): test_search_agents(request_type=dict) +def test_search_agents_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_agents), "__call__") as call: + client.search_agents() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.SearchAgentsRequest() + + @pytest.mark.asyncio async def test_search_agents_async( transport: str = "grpc_asyncio", request_type=agent.SearchAgentsRequest @@ -1432,6 +1499,22 @@ def test_train_agent_from_dict(): test_train_agent(request_type=dict) +def test_train_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.train_agent), "__call__") as call: + client.train_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.TrainAgentRequest() + + @pytest.mark.asyncio async def test_train_agent_async( transport: str = "grpc_asyncio", request_type=agent.TrainAgentRequest @@ -1616,6 +1699,22 @@ def test_export_agent_from_dict(): test_export_agent(request_type=dict) +def test_export_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.export_agent), "__call__") as call: + client.export_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.ExportAgentRequest() + + @pytest.mark.asyncio async def test_export_agent_async( transport: str = "grpc_asyncio", request_type=agent.ExportAgentRequest @@ -1800,6 +1899,22 @@ def test_import_agent_from_dict(): test_import_agent(request_type=dict) +def test_import_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.import_agent), "__call__") as call: + client.import_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.ImportAgentRequest() + + @pytest.mark.asyncio async def test_import_agent_async( transport: str = "grpc_asyncio", request_type=agent.ImportAgentRequest @@ -1917,6 +2032,22 @@ def test_restore_agent_from_dict(): test_restore_agent(request_type=dict) +def test_restore_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.restore_agent), "__call__") as call: + client.restore_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.RestoreAgentRequest() + + @pytest.mark.asyncio async def test_restore_agent_async( transport: str = "grpc_asyncio", request_type=agent.RestoreAgentRequest @@ -2039,6 +2170,24 @@ def test_get_validation_result_from_dict(): test_get_validation_result(request_type=dict) +def test_get_validation_result_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_validation_result), "__call__" + ) as call: + client.get_validation_result() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.GetValidationResultRequest() + + @pytest.mark.asyncio async def test_get_validation_result_async( transport: str = "grpc_asyncio", request_type=agent.GetValidationResultRequest @@ -2188,7 +2337,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], + [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -2310,6 +2459,51 @@ def test_agents_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], +) +def test_agents_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_agents_host_no_port(): client = AgentsClient( credentials=credentials.AnonymousCredentials(), @@ -2331,7 +2525,7 @@ def test_agents_host_with_port(): def test_agents_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.AgentsGrpcTransport( @@ -2343,7 +2537,7 @@ def test_agents_grpc_transport_channel(): def test_agents_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.AgentsGrpcAsyncIOTransport( @@ -2354,6 +2548,8 @@ def test_agents_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], @@ -2363,7 +2559,7 @@ def test_agents_transport_channel_mtls_with_client_cert_source(transport_class): "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -2404,6 +2600,8 @@ def test_agents_transport_channel_mtls_with_client_cert_source(transport_class): assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], @@ -2416,7 +2614,7 @@ def test_agents_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2/test_answer_records.py b/tests/unit/gapic/dialogflow_v2/test_answer_records.py new file mode 100644 index 000000000..39c5932a3 --- /dev/null +++ b/tests/unit/gapic/dialogflow_v2/test_answer_records.py @@ -0,0 +1,1598 @@ +# -*- 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.dialogflow_v2.services.answer_records import AnswerRecordsAsyncClient +from google.cloud.dialogflow_v2.services.answer_records import AnswerRecordsClient +from google.cloud.dialogflow_v2.services.answer_records import pagers +from google.cloud.dialogflow_v2.services.answer_records import transports +from google.cloud.dialogflow_v2.types import answer_record +from google.cloud.dialogflow_v2.types import answer_record as gcd_answer_record +from google.cloud.dialogflow_v2.types import participant +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # 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 AnswerRecordsClient._get_default_mtls_endpoint(None) is None + assert ( + AnswerRecordsClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + AnswerRecordsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + AnswerRecordsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + AnswerRecordsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + AnswerRecordsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [AnswerRecordsClient, AnswerRecordsAsyncClient,] +) +def test_answer_records_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize( + "client_class", [AnswerRecordsClient, AnswerRecordsAsyncClient,] +) +def test_answer_records_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 + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_answer_records_client_get_transport_class(): + transport = AnswerRecordsClient.get_transport_class() + available_transports = [ + transports.AnswerRecordsGrpcTransport, + ] + assert transport in available_transports + + transport = AnswerRecordsClient.get_transport_class("grpc") + assert transport == transports.AnswerRecordsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (AnswerRecordsClient, transports.AnswerRecordsGrpcTransport, "grpc"), + ( + AnswerRecordsAsyncClient, + transports.AnswerRecordsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + AnswerRecordsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(AnswerRecordsClient), +) +@mock.patch.object( + AnswerRecordsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(AnswerRecordsAsyncClient), +) +def test_answer_records_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(AnswerRecordsClient, "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(AnswerRecordsClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (AnswerRecordsClient, transports.AnswerRecordsGrpcTransport, "grpc", "true"), + ( + AnswerRecordsAsyncClient, + transports.AnswerRecordsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (AnswerRecordsClient, transports.AnswerRecordsGrpcTransport, "grpc", "false"), + ( + AnswerRecordsAsyncClient, + transports.AnswerRecordsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + AnswerRecordsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(AnswerRecordsClient), +) +@mock.patch.object( + AnswerRecordsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(AnswerRecordsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_answer_records_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (AnswerRecordsClient, transports.AnswerRecordsGrpcTransport, "grpc"), + ( + AnswerRecordsAsyncClient, + transports.AnswerRecordsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_answer_records_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (AnswerRecordsClient, transports.AnswerRecordsGrpcTransport, "grpc"), + ( + AnswerRecordsAsyncClient, + transports.AnswerRecordsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_answer_records_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_answer_records_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflow_v2.services.answer_records.transports.AnswerRecordsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = AnswerRecordsClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_answer_records( + transport: str = "grpc", request_type=answer_record.ListAnswerRecordsRequest +): + client = AnswerRecordsClient( + 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_answer_records), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = answer_record.ListAnswerRecordsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_answer_records(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == answer_record.ListAnswerRecordsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListAnswerRecordsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_answer_records_from_dict(): + test_list_answer_records(request_type=dict) + + +def test_list_answer_records_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + client.list_answer_records() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == answer_record.ListAnswerRecordsRequest() + + +@pytest.mark.asyncio +async def test_list_answer_records_async( + transport: str = "grpc_asyncio", request_type=answer_record.ListAnswerRecordsRequest +): + client = AnswerRecordsAsyncClient( + 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_answer_records), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + answer_record.ListAnswerRecordsResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_answer_records(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == answer_record.ListAnswerRecordsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListAnswerRecordsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_answer_records_async_from_dict(): + await test_list_answer_records_async(request_type=dict) + + +def test_list_answer_records_field_headers(): + client = AnswerRecordsClient(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 = answer_record.ListAnswerRecordsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + call.return_value = answer_record.ListAnswerRecordsResponse() + + client.list_answer_records(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_answer_records_field_headers_async(): + client = AnswerRecordsAsyncClient(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 = answer_record.ListAnswerRecordsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + answer_record.ListAnswerRecordsResponse() + ) + + await client.list_answer_records(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_answer_records_flattened(): + client = AnswerRecordsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = answer_record.ListAnswerRecordsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_answer_records(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_answer_records_flattened_error(): + client = AnswerRecordsClient(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_answer_records( + answer_record.ListAnswerRecordsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_answer_records_flattened_async(): + client = AnswerRecordsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = answer_record.ListAnswerRecordsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + answer_record.ListAnswerRecordsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_answer_records(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_answer_records_flattened_error_async(): + client = AnswerRecordsAsyncClient(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_answer_records( + answer_record.ListAnswerRecordsRequest(), parent="parent_value", + ) + + +def test_list_answer_records_pager(): + client = AnswerRecordsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + next_page_token="abc", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[], next_page_token="def", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[answer_record.AnswerRecord(),], next_page_token="ghi", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_answer_records(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, answer_record.AnswerRecord) for i in results) + + +def test_list_answer_records_pages(): + client = AnswerRecordsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + next_page_token="abc", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[], next_page_token="def", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[answer_record.AnswerRecord(),], next_page_token="ghi", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + ), + RuntimeError, + ) + pages = list(client.list_answer_records(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_answer_records_async_pager(): + client = AnswerRecordsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + next_page_token="abc", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[], next_page_token="def", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[answer_record.AnswerRecord(),], next_page_token="ghi", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + ), + RuntimeError, + ) + async_pager = await client.list_answer_records(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, answer_record.AnswerRecord) for i in responses) + + +@pytest.mark.asyncio +async def test_list_answer_records_async_pages(): + client = AnswerRecordsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + next_page_token="abc", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[], next_page_token="def", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[answer_record.AnswerRecord(),], next_page_token="ghi", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_answer_records(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_update_answer_record( + transport: str = "grpc", request_type=gcd_answer_record.UpdateAnswerRecordRequest +): + client = AnswerRecordsClient( + 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_answer_record), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_answer_record.AnswerRecord( + name="name_value", + agent_assistant_record=gcd_answer_record.AgentAssistantRecord( + article_suggestion_answer=participant.ArticleAnswer(title="title_value") + ), + ) + + response = client.update_answer_record(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_answer_record.UpdateAnswerRecordRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_answer_record.AnswerRecord) + + assert response.name == "name_value" + + +def test_update_answer_record_from_dict(): + test_update_answer_record(request_type=dict) + + +def test_update_answer_record_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_answer_record), "__call__" + ) as call: + client.update_answer_record() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_answer_record.UpdateAnswerRecordRequest() + + +@pytest.mark.asyncio +async def test_update_answer_record_async( + transport: str = "grpc_asyncio", + request_type=gcd_answer_record.UpdateAnswerRecordRequest, +): + client = AnswerRecordsAsyncClient( + 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_answer_record), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_answer_record.AnswerRecord(name="name_value",) + ) + + response = await client.update_answer_record(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_answer_record.UpdateAnswerRecordRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_answer_record.AnswerRecord) + + assert response.name == "name_value" + + +@pytest.mark.asyncio +async def test_update_answer_record_async_from_dict(): + await test_update_answer_record_async(request_type=dict) + + +def test_update_answer_record_field_headers(): + client = AnswerRecordsClient(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 = gcd_answer_record.UpdateAnswerRecordRequest() + request.answer_record.name = "answer_record.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_answer_record), "__call__" + ) as call: + call.return_value = gcd_answer_record.AnswerRecord() + + client.update_answer_record(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", + "answer_record.name=answer_record.name/value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_answer_record_field_headers_async(): + client = AnswerRecordsAsyncClient(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 = gcd_answer_record.UpdateAnswerRecordRequest() + request.answer_record.name = "answer_record.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_answer_record), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_answer_record.AnswerRecord() + ) + + await client.update_answer_record(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", + "answer_record.name=answer_record.name/value", + ) in kw["metadata"] + + +def test_update_answer_record_flattened(): + client = AnswerRecordsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_answer_record), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_answer_record.AnswerRecord() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_answer_record( + answer_record=gcd_answer_record.AnswerRecord(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].answer_record == gcd_answer_record.AnswerRecord( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_answer_record_flattened_error(): + client = AnswerRecordsClient(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_answer_record( + gcd_answer_record.UpdateAnswerRecordRequest(), + answer_record=gcd_answer_record.AnswerRecord(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_answer_record_flattened_async(): + client = AnswerRecordsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_answer_record), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_answer_record.AnswerRecord() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_answer_record.AnswerRecord() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_answer_record( + answer_record=gcd_answer_record.AnswerRecord(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].answer_record == gcd_answer_record.AnswerRecord( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_answer_record_flattened_error_async(): + client = AnswerRecordsAsyncClient(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_answer_record( + gcd_answer_record.UpdateAnswerRecordRequest(), + answer_record=gcd_answer_record.AnswerRecord(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.AnswerRecordsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.AnswerRecordsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = AnswerRecordsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.AnswerRecordsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = AnswerRecordsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.AnswerRecordsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = AnswerRecordsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.AnswerRecordsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.AnswerRecordsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.AnswerRecordsGrpcTransport, + transports.AnswerRecordsGrpcAsyncIOTransport, + ], +) +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 = AnswerRecordsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.AnswerRecordsGrpcTransport,) + + +def test_answer_records_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.AnswerRecordsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_answer_records_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflow_v2.services.answer_records.transports.AnswerRecordsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.AnswerRecordsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_answer_records", + "update_answer_record", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_answer_records_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.dialogflow_v2.services.answer_records.transports.AnswerRecordsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.AnswerRecordsTransport( + 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_answer_records_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.dialogflow_v2.services.answer_records.transports.AnswerRecordsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.AnswerRecordsTransport() + adc.assert_called_once() + + +def test_answer_records_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) + AnswerRecordsClient() + 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_answer_records_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.AnswerRecordsGrpcTransport( + 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", + ) + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.AnswerRecordsGrpcTransport, + transports.AnswerRecordsGrpcAsyncIOTransport, + ], +) +def test_answer_records_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_answer_records_host_no_port(): + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_answer_records_host_with_port(): + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_answer_records_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.AnswerRecordsGrpcTransport( + 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_answer_records_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.AnswerRecordsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.AnswerRecordsGrpcTransport, + transports.AnswerRecordsGrpcAsyncIOTransport, + ], +) +def test_answer_records_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.AnswerRecordsGrpcTransport, + transports.AnswerRecordsGrpcAsyncIOTransport, + ], +) +def test_answer_records_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_answer_record_path(): + project = "squid" + answer_record = "clam" + + expected = "projects/{project}/answerRecords/{answer_record}".format( + project=project, answer_record=answer_record, + ) + actual = AnswerRecordsClient.answer_record_path(project, answer_record) + assert expected == actual + + +def test_parse_answer_record_path(): + expected = { + "project": "whelk", + "answer_record": "octopus", + } + path = AnswerRecordsClient.answer_record_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.parse_answer_record_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "oyster" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = AnswerRecordsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nudibranch", + } + path = AnswerRecordsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "cuttlefish" + + expected = "folders/{folder}".format(folder=folder,) + actual = AnswerRecordsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "mussel", + } + path = AnswerRecordsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "winkle" + + expected = "organizations/{organization}".format(organization=organization,) + actual = AnswerRecordsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nautilus", + } + path = AnswerRecordsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "scallop" + + expected = "projects/{project}".format(project=project,) + actual = AnswerRecordsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "abalone", + } + path = AnswerRecordsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.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 = AnswerRecordsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "whelk", + "location": "octopus", + } + path = AnswerRecordsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.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.AnswerRecordsTransport, "_prep_wrapped_messages" + ) as prep: + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.AnswerRecordsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = AnswerRecordsClient.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/dialogflow_v2/test_contexts.py b/tests/unit/gapic/dialogflow_v2/test_contexts.py index 814852823..8d2548366 100644 --- a/tests/unit/gapic/dialogflow_v2/test_contexts.py +++ b/tests/unit/gapic/dialogflow_v2/test_contexts.py @@ -82,7 +82,22 @@ def test__get_default_mtls_endpoint(): assert ContextsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [ContextsClient, ContextsAsyncClient]) +@pytest.mark.parametrize("client_class", [ContextsClient, ContextsAsyncClient,]) +def test_contexts_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [ContextsClient, ContextsAsyncClient,]) def test_contexts_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -91,16 +106,21 @@ def test_contexts_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_contexts_client_get_transport_class(): transport = ContextsClient.get_transport_class() - assert transport == transports.ContextsGrpcTransport + available_transports = [ + transports.ContextsGrpcTransport, + ] + assert transport in available_transports transport = ContextsClient.get_transport_class("grpc") assert transport == transports.ContextsGrpcTransport @@ -143,7 +163,7 @@ def test_contexts_client_client_options(client_class, transport_class, transport credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -159,7 +179,7 @@ def test_contexts_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -175,7 +195,7 @@ def test_contexts_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -203,7 +223,7 @@ def test_contexts_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -252,29 +272,25 @@ def test_contexts_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -283,66 +299,53 @@ def test_contexts_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -364,7 +367,7 @@ def test_contexts_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -390,7 +393,7 @@ def test_contexts_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -407,7 +410,7 @@ def test_contexts_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -450,6 +453,22 @@ def test_list_contexts_from_dict(): test_list_contexts(request_type=dict) +def test_list_contexts_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_contexts), "__call__") as call: + client.list_contexts() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == context.ListContextsRequest() + + @pytest.mark.asyncio async def test_list_contexts_async( transport: str = "grpc_asyncio", request_type=context.ListContextsRequest @@ -763,6 +782,22 @@ def test_get_context_from_dict(): test_get_context(request_type=dict) +def test_get_context_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_context), "__call__") as call: + client.get_context() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == context.GetContextRequest() + + @pytest.mark.asyncio async def test_get_context_async( transport: str = "grpc_asyncio", request_type=context.GetContextRequest @@ -954,6 +989,22 @@ def test_create_context_from_dict(): test_create_context(request_type=dict) +def test_create_context_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_context), "__call__") as call: + client.create_context() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_context.CreateContextRequest() + + @pytest.mark.asyncio async def test_create_context_async( transport: str = "grpc_asyncio", request_type=gcd_context.CreateContextRequest @@ -1157,6 +1208,22 @@ def test_update_context_from_dict(): test_update_context(request_type=dict) +def test_update_context_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_context), "__call__") as call: + client.update_context() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_context.UpdateContextRequest() + + @pytest.mark.asyncio async def test_update_context_async( transport: str = "grpc_asyncio", request_type=gcd_context.UpdateContextRequest @@ -1361,6 +1428,22 @@ def test_delete_context_from_dict(): test_delete_context(request_type=dict) +def test_delete_context_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_context), "__call__") as call: + client.delete_context() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == context.DeleteContextRequest() + + @pytest.mark.asyncio async def test_delete_context_async( transport: str = "grpc_asyncio", request_type=context.DeleteContextRequest @@ -1543,6 +1626,24 @@ def test_delete_all_contexts_from_dict(): test_delete_all_contexts(request_type=dict) +def test_delete_all_contexts_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_all_contexts), "__call__" + ) as call: + client.delete_all_contexts() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == context.DeleteAllContextsRequest() + + @pytest.mark.asyncio async def test_delete_all_contexts_async( transport: str = "grpc_asyncio", request_type=context.DeleteAllContextsRequest @@ -1757,7 +1858,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.ContextsGrpcTransport, transports.ContextsGrpcAsyncIOTransport], + [transports.ContextsGrpcTransport, transports.ContextsGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -1871,6 +1972,51 @@ def test_contexts_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.ContextsGrpcTransport, transports.ContextsGrpcAsyncIOTransport], +) +def test_contexts_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_contexts_host_no_port(): client = ContextsClient( credentials=credentials.AnonymousCredentials(), @@ -1892,7 +2038,7 @@ def test_contexts_host_with_port(): def test_contexts_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.ContextsGrpcTransport( @@ -1904,7 +2050,7 @@ def test_contexts_grpc_transport_channel(): def test_contexts_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.ContextsGrpcAsyncIOTransport( @@ -1915,6 +2061,8 @@ def test_contexts_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.ContextsGrpcTransport, transports.ContextsGrpcAsyncIOTransport], @@ -1924,7 +2072,7 @@ def test_contexts_transport_channel_mtls_with_client_cert_source(transport_class "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -1965,6 +2113,8 @@ def test_contexts_transport_channel_mtls_with_client_cert_source(transport_class assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.ContextsGrpcTransport, transports.ContextsGrpcAsyncIOTransport], @@ -1977,7 +2127,7 @@ def test_contexts_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2/test_conversation_profiles.py b/tests/unit/gapic/dialogflow_v2/test_conversation_profiles.py new file mode 100644 index 000000000..d4ef7e529 --- /dev/null +++ b/tests/unit/gapic/dialogflow_v2/test_conversation_profiles.py @@ -0,0 +1,2493 @@ +# -*- 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.dialogflow_v2.services.conversation_profiles import ( + ConversationProfilesAsyncClient, +) +from google.cloud.dialogflow_v2.services.conversation_profiles import ( + ConversationProfilesClient, +) +from google.cloud.dialogflow_v2.services.conversation_profiles import pagers +from google.cloud.dialogflow_v2.services.conversation_profiles import transports +from google.cloud.dialogflow_v2.types import audio_config +from google.cloud.dialogflow_v2.types import conversation_profile +from google.cloud.dialogflow_v2.types import ( + conversation_profile as gcd_conversation_profile, +) +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # 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 ConversationProfilesClient._get_default_mtls_endpoint(None) is None + assert ( + ConversationProfilesClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + ConversationProfilesClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + ConversationProfilesClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ConversationProfilesClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ConversationProfilesClient._get_default_mtls_endpoint(non_googleapi) + == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [ConversationProfilesClient, ConversationProfilesAsyncClient,] +) +def test_conversation_profiles_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize( + "client_class", [ConversationProfilesClient, ConversationProfilesAsyncClient,] +) +def test_conversation_profiles_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 + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_conversation_profiles_client_get_transport_class(): + transport = ConversationProfilesClient.get_transport_class() + available_transports = [ + transports.ConversationProfilesGrpcTransport, + ] + assert transport in available_transports + + transport = ConversationProfilesClient.get_transport_class("grpc") + assert transport == transports.ConversationProfilesGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + ConversationProfilesClient, + transports.ConversationProfilesGrpcTransport, + "grpc", + ), + ( + ConversationProfilesAsyncClient, + transports.ConversationProfilesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + ConversationProfilesClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationProfilesClient), +) +@mock.patch.object( + ConversationProfilesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationProfilesAsyncClient), +) +def test_conversation_profiles_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(ConversationProfilesClient, "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(ConversationProfilesClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + ( + ConversationProfilesClient, + transports.ConversationProfilesGrpcTransport, + "grpc", + "true", + ), + ( + ConversationProfilesAsyncClient, + transports.ConversationProfilesGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + ( + ConversationProfilesClient, + transports.ConversationProfilesGrpcTransport, + "grpc", + "false", + ), + ( + ConversationProfilesAsyncClient, + transports.ConversationProfilesGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + ConversationProfilesClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationProfilesClient), +) +@mock.patch.object( + ConversationProfilesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationProfilesAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_conversation_profiles_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + ConversationProfilesClient, + transports.ConversationProfilesGrpcTransport, + "grpc", + ), + ( + ConversationProfilesAsyncClient, + transports.ConversationProfilesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_conversation_profiles_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + ConversationProfilesClient, + transports.ConversationProfilesGrpcTransport, + "grpc", + ), + ( + ConversationProfilesAsyncClient, + transports.ConversationProfilesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_conversation_profiles_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_conversation_profiles_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflow_v2.services.conversation_profiles.transports.ConversationProfilesGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = ConversationProfilesClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_conversation_profiles( + transport: str = "grpc", + request_type=conversation_profile.ListConversationProfilesRequest, +): + client = ConversationProfilesClient( + 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_conversation_profiles), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ListConversationProfilesResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_conversation_profiles(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.ListConversationProfilesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListConversationProfilesPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_conversation_profiles_from_dict(): + test_list_conversation_profiles(request_type=dict) + + +def test_list_conversation_profiles_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + client.list_conversation_profiles() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.ListConversationProfilesRequest() + + +@pytest.mark.asyncio +async def test_list_conversation_profiles_async( + transport: str = "grpc_asyncio", + request_type=conversation_profile.ListConversationProfilesRequest, +): + client = ConversationProfilesAsyncClient( + 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_conversation_profiles), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ListConversationProfilesResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_conversation_profiles(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.ListConversationProfilesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListConversationProfilesAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_conversation_profiles_async_from_dict(): + await test_list_conversation_profiles_async(request_type=dict) + + +def test_list_conversation_profiles_field_headers(): + client = ConversationProfilesClient(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 = conversation_profile.ListConversationProfilesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + call.return_value = conversation_profile.ListConversationProfilesResponse() + + client.list_conversation_profiles(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_conversation_profiles_field_headers_async(): + client = ConversationProfilesAsyncClient( + 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 = conversation_profile.ListConversationProfilesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ListConversationProfilesResponse() + ) + + await client.list_conversation_profiles(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_conversation_profiles_flattened(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ListConversationProfilesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_conversation_profiles(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_conversation_profiles_flattened_error(): + client = ConversationProfilesClient(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_conversation_profiles( + conversation_profile.ListConversationProfilesRequest(), + parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_conversation_profiles_flattened_async(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ListConversationProfilesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ListConversationProfilesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_conversation_profiles(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_conversation_profiles_flattened_error_async(): + client = ConversationProfilesAsyncClient( + 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_conversation_profiles( + conversation_profile.ListConversationProfilesRequest(), + parent="parent_value", + ) + + +def test_list_conversation_profiles_pager(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + next_page_token="abc", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[], next_page_token="def", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[conversation_profile.ConversationProfile(),], + next_page_token="ghi", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_conversation_profiles(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all( + isinstance(i, conversation_profile.ConversationProfile) for i in results + ) + + +def test_list_conversation_profiles_pages(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + next_page_token="abc", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[], next_page_token="def", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[conversation_profile.ConversationProfile(),], + next_page_token="ghi", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + ), + RuntimeError, + ) + pages = list(client.list_conversation_profiles(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_conversation_profiles_async_pager(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + next_page_token="abc", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[], next_page_token="def", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[conversation_profile.ConversationProfile(),], + next_page_token="ghi", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + ), + RuntimeError, + ) + async_pager = await client.list_conversation_profiles(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, conversation_profile.ConversationProfile) for i in responses + ) + + +@pytest.mark.asyncio +async def test_list_conversation_profiles_async_pages(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + next_page_token="abc", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[], next_page_token="def", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[conversation_profile.ConversationProfile(),], + next_page_token="ghi", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_conversation_profiles(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_conversation_profile( + transport: str = "grpc", + request_type=conversation_profile.GetConversationProfileRequest, +): + client = ConversationProfilesClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + + response = client.get_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.GetConversationProfileRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +def test_get_conversation_profile_from_dict(): + test_get_conversation_profile(request_type=dict) + + +def test_get_conversation_profile_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_conversation_profile), "__call__" + ) as call: + client.get_conversation_profile() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.GetConversationProfileRequest() + + +@pytest.mark.asyncio +async def test_get_conversation_profile_async( + transport: str = "grpc_asyncio", + request_type=conversation_profile.GetConversationProfileRequest, +): + client = ConversationProfilesAsyncClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + ) + + response = await client.get_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.GetConversationProfileRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +@pytest.mark.asyncio +async def test_get_conversation_profile_async_from_dict(): + await test_get_conversation_profile_async(request_type=dict) + + +def test_get_conversation_profile_field_headers(): + client = ConversationProfilesClient(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 = conversation_profile.GetConversationProfileRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_conversation_profile), "__call__" + ) as call: + call.return_value = conversation_profile.ConversationProfile() + + client.get_conversation_profile(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_conversation_profile_field_headers_async(): + client = ConversationProfilesAsyncClient( + 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 = conversation_profile.GetConversationProfileRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_conversation_profile), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ConversationProfile() + ) + + await client.get_conversation_profile(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_conversation_profile_flattened(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ConversationProfile() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_conversation_profile(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_conversation_profile_flattened_error(): + client = ConversationProfilesClient(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_conversation_profile( + conversation_profile.GetConversationProfileRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_conversation_profile_flattened_async(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ConversationProfile() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ConversationProfile() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_conversation_profile(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_conversation_profile_flattened_error_async(): + client = ConversationProfilesAsyncClient( + 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_conversation_profile( + conversation_profile.GetConversationProfileRequest(), name="name_value", + ) + + +def test_create_conversation_profile( + transport: str = "grpc", + request_type=gcd_conversation_profile.CreateConversationProfileRequest, +): + client = ConversationProfilesClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + + response = client.create_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.CreateConversationProfileRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +def test_create_conversation_profile_from_dict(): + test_create_conversation_profile(request_type=dict) + + +def test_create_conversation_profile_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation_profile), "__call__" + ) as call: + client.create_conversation_profile() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.CreateConversationProfileRequest() + + +@pytest.mark.asyncio +async def test_create_conversation_profile_async( + transport: str = "grpc_asyncio", + request_type=gcd_conversation_profile.CreateConversationProfileRequest, +): + client = ConversationProfilesAsyncClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + ) + + response = await client.create_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.CreateConversationProfileRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +@pytest.mark.asyncio +async def test_create_conversation_profile_async_from_dict(): + await test_create_conversation_profile_async(request_type=dict) + + +def test_create_conversation_profile_field_headers(): + client = ConversationProfilesClient(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 = gcd_conversation_profile.CreateConversationProfileRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation_profile), "__call__" + ) as call: + call.return_value = gcd_conversation_profile.ConversationProfile() + + client.create_conversation_profile(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_conversation_profile_field_headers_async(): + client = ConversationProfilesAsyncClient( + 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 = gcd_conversation_profile.CreateConversationProfileRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation_profile), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile() + ) + + await client.create_conversation_profile(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_conversation_profile_flattened(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_conversation_profile( + parent="parent_value", + conversation_profile=gcd_conversation_profile.ConversationProfile( + 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 + ].conversation_profile == gcd_conversation_profile.ConversationProfile( + name="name_value" + ) + + +def test_create_conversation_profile_flattened_error(): + client = ConversationProfilesClient(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_conversation_profile( + gcd_conversation_profile.CreateConversationProfileRequest(), + parent="parent_value", + conversation_profile=gcd_conversation_profile.ConversationProfile( + name="name_value" + ), + ) + + +@pytest.mark.asyncio +async def test_create_conversation_profile_flattened_async(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_conversation_profile( + parent="parent_value", + conversation_profile=gcd_conversation_profile.ConversationProfile( + 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 + ].conversation_profile == gcd_conversation_profile.ConversationProfile( + name="name_value" + ) + + +@pytest.mark.asyncio +async def test_create_conversation_profile_flattened_error_async(): + client = ConversationProfilesAsyncClient( + 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_conversation_profile( + gcd_conversation_profile.CreateConversationProfileRequest(), + parent="parent_value", + conversation_profile=gcd_conversation_profile.ConversationProfile( + name="name_value" + ), + ) + + +def test_update_conversation_profile( + transport: str = "grpc", + request_type=gcd_conversation_profile.UpdateConversationProfileRequest, +): + client = ConversationProfilesClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + + response = client.update_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.UpdateConversationProfileRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +def test_update_conversation_profile_from_dict(): + test_update_conversation_profile(request_type=dict) + + +def test_update_conversation_profile_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_conversation_profile), "__call__" + ) as call: + client.update_conversation_profile() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.UpdateConversationProfileRequest() + + +@pytest.mark.asyncio +async def test_update_conversation_profile_async( + transport: str = "grpc_asyncio", + request_type=gcd_conversation_profile.UpdateConversationProfileRequest, +): + client = ConversationProfilesAsyncClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + ) + + response = await client.update_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.UpdateConversationProfileRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +@pytest.mark.asyncio +async def test_update_conversation_profile_async_from_dict(): + await test_update_conversation_profile_async(request_type=dict) + + +def test_update_conversation_profile_field_headers(): + client = ConversationProfilesClient(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 = gcd_conversation_profile.UpdateConversationProfileRequest() + request.conversation_profile.name = "conversation_profile.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_conversation_profile), "__call__" + ) as call: + call.return_value = gcd_conversation_profile.ConversationProfile() + + client.update_conversation_profile(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", + "conversation_profile.name=conversation_profile.name/value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_conversation_profile_field_headers_async(): + client = ConversationProfilesAsyncClient( + 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 = gcd_conversation_profile.UpdateConversationProfileRequest() + request.conversation_profile.name = "conversation_profile.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_conversation_profile), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile() + ) + + await client.update_conversation_profile(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", + "conversation_profile.name=conversation_profile.name/value", + ) in kw["metadata"] + + +def test_update_conversation_profile_flattened(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_conversation_profile( + conversation_profile=gcd_conversation_profile.ConversationProfile( + 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 + ].conversation_profile == gcd_conversation_profile.ConversationProfile( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_conversation_profile_flattened_error(): + client = ConversationProfilesClient(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_conversation_profile( + gcd_conversation_profile.UpdateConversationProfileRequest(), + conversation_profile=gcd_conversation_profile.ConversationProfile( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_conversation_profile_flattened_async(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_conversation_profile( + conversation_profile=gcd_conversation_profile.ConversationProfile( + 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 + ].conversation_profile == gcd_conversation_profile.ConversationProfile( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_conversation_profile_flattened_error_async(): + client = ConversationProfilesAsyncClient( + 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_conversation_profile( + gcd_conversation_profile.UpdateConversationProfileRequest(), + conversation_profile=gcd_conversation_profile.ConversationProfile( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_conversation_profile( + transport: str = "grpc", + request_type=conversation_profile.DeleteConversationProfileRequest, +): + client = ConversationProfilesClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.DeleteConversationProfileRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_conversation_profile_from_dict(): + test_delete_conversation_profile(request_type=dict) + + +def test_delete_conversation_profile_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_conversation_profile), "__call__" + ) as call: + client.delete_conversation_profile() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.DeleteConversationProfileRequest() + + +@pytest.mark.asyncio +async def test_delete_conversation_profile_async( + transport: str = "grpc_asyncio", + request_type=conversation_profile.DeleteConversationProfileRequest, +): + client = ConversationProfilesAsyncClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.DeleteConversationProfileRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_conversation_profile_async_from_dict(): + await test_delete_conversation_profile_async(request_type=dict) + + +def test_delete_conversation_profile_field_headers(): + client = ConversationProfilesClient(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 = conversation_profile.DeleteConversationProfileRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_conversation_profile), "__call__" + ) as call: + call.return_value = None + + client.delete_conversation_profile(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_conversation_profile_field_headers_async(): + client = ConversationProfilesAsyncClient( + 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 = conversation_profile.DeleteConversationProfileRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_conversation_profile), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_conversation_profile(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_conversation_profile_flattened(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_conversation_profile), "__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_conversation_profile(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_conversation_profile_flattened_error(): + client = ConversationProfilesClient(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_conversation_profile( + conversation_profile.DeleteConversationProfileRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_conversation_profile_flattened_async(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_conversation_profile), "__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_conversation_profile(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_conversation_profile_flattened_error_async(): + client = ConversationProfilesAsyncClient( + 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_conversation_profile( + conversation_profile.DeleteConversationProfileRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.ConversationProfilesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.ConversationProfilesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationProfilesClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.ConversationProfilesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationProfilesClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.ConversationProfilesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = ConversationProfilesClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.ConversationProfilesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.ConversationProfilesGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationProfilesGrpcTransport, + transports.ConversationProfilesGrpcAsyncIOTransport, + ], +) +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 = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.ConversationProfilesGrpcTransport,) + + +def test_conversation_profiles_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.ConversationProfilesTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_conversation_profiles_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflow_v2.services.conversation_profiles.transports.ConversationProfilesTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.ConversationProfilesTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_conversation_profiles", + "get_conversation_profile", + "create_conversation_profile", + "update_conversation_profile", + "delete_conversation_profile", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_conversation_profiles_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.dialogflow_v2.services.conversation_profiles.transports.ConversationProfilesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ConversationProfilesTransport( + 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_conversation_profiles_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.dialogflow_v2.services.conversation_profiles.transports.ConversationProfilesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ConversationProfilesTransport() + adc.assert_called_once() + + +def test_conversation_profiles_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) + ConversationProfilesClient() + 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_conversation_profiles_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.ConversationProfilesGrpcTransport( + 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", + ) + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationProfilesGrpcTransport, + transports.ConversationProfilesGrpcAsyncIOTransport, + ], +) +def test_conversation_profiles_grpc_transport_client_cert_source_for_mtls( + transport_class, +): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_conversation_profiles_host_no_port(): + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_conversation_profiles_host_with_port(): + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_conversation_profiles_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ConversationProfilesGrpcTransport( + 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_conversation_profiles_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ConversationProfilesGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationProfilesGrpcTransport, + transports.ConversationProfilesGrpcAsyncIOTransport, + ], +) +def test_conversation_profiles_transport_channel_mtls_with_client_cert_source( + transport_class, +): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationProfilesGrpcTransport, + transports.ConversationProfilesGrpcAsyncIOTransport, + ], +) +def test_conversation_profiles_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_agent_path(): + project = "squid" + + expected = "projects/{project}/agent".format(project=project,) + actual = ConversationProfilesClient.agent_path(project) + assert expected == actual + + +def test_parse_agent_path(): + expected = { + "project": "clam", + } + path = ConversationProfilesClient.agent_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_agent_path(path) + assert expected == actual + + +def test_conversation_profile_path(): + project = "whelk" + conversation_profile = "octopus" + + expected = "projects/{project}/conversationProfiles/{conversation_profile}".format( + project=project, conversation_profile=conversation_profile, + ) + actual = ConversationProfilesClient.conversation_profile_path( + project, conversation_profile + ) + assert expected == actual + + +def test_parse_conversation_profile_path(): + expected = { + "project": "oyster", + "conversation_profile": "nudibranch", + } + path = ConversationProfilesClient.conversation_profile_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_conversation_profile_path(path) + assert expected == actual + + +def test_document_path(): + project = "cuttlefish" + knowledge_base = "mussel" + document = "winkle" + + expected = "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, knowledge_base=knowledge_base, document=document, + ) + actual = ConversationProfilesClient.document_path(project, knowledge_base, document) + assert expected == actual + + +def test_parse_document_path(): + expected = { + "project": "nautilus", + "knowledge_base": "scallop", + "document": "abalone", + } + path = ConversationProfilesClient.document_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_document_path(path) + assert expected == actual + + +def test_knowledge_base_path(): + project = "squid" + knowledge_base = "clam" + + expected = "projects/{project}/knowledgeBases/{knowledge_base}".format( + project=project, knowledge_base=knowledge_base, + ) + actual = ConversationProfilesClient.knowledge_base_path(project, knowledge_base) + assert expected == actual + + +def test_parse_knowledge_base_path(): + expected = { + "project": "whelk", + "knowledge_base": "octopus", + } + path = ConversationProfilesClient.knowledge_base_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_knowledge_base_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "oyster" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = ConversationProfilesClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nudibranch", + } + path = ConversationProfilesClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "cuttlefish" + + expected = "folders/{folder}".format(folder=folder,) + actual = ConversationProfilesClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "mussel", + } + path = ConversationProfilesClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "winkle" + + expected = "organizations/{organization}".format(organization=organization,) + actual = ConversationProfilesClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nautilus", + } + path = ConversationProfilesClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "scallop" + + expected = "projects/{project}".format(project=project,) + actual = ConversationProfilesClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "abalone", + } + path = ConversationProfilesClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.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 = ConversationProfilesClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "whelk", + "location": "octopus", + } + path = ConversationProfilesClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.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.ConversationProfilesTransport, "_prep_wrapped_messages" + ) as prep: + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.ConversationProfilesTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = ConversationProfilesClient.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/dialogflow_v2/test_conversations.py b/tests/unit/gapic/dialogflow_v2/test_conversations.py new file mode 100644 index 000000000..2fedf55b8 --- /dev/null +++ b/tests/unit/gapic/dialogflow_v2/test_conversations.py @@ -0,0 +1,3383 @@ +# -*- 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.dialogflow_v2.services.conversations import ConversationsAsyncClient +from google.cloud.dialogflow_v2.services.conversations import ConversationsClient +from google.cloud.dialogflow_v2.services.conversations import pagers +from google.cloud.dialogflow_v2.services.conversations import transports +from google.cloud.dialogflow_v2.types import conversation +from google.cloud.dialogflow_v2.types import conversation as gcd_conversation +from google.cloud.dialogflow_v2.types import participant +from google.oauth2 import service_account +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 ConversationsClient._get_default_mtls_endpoint(None) is None + assert ( + ConversationsClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + ConversationsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + ConversationsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ConversationsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ConversationsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [ConversationsClient, ConversationsAsyncClient,] +) +def test_conversations_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize( + "client_class", [ConversationsClient, ConversationsAsyncClient,] +) +def test_conversations_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 + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_conversations_client_get_transport_class(): + transport = ConversationsClient.get_transport_class() + available_transports = [ + transports.ConversationsGrpcTransport, + ] + assert transport in available_transports + + transport = ConversationsClient.get_transport_class("grpc") + assert transport == transports.ConversationsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ConversationsClient, transports.ConversationsGrpcTransport, "grpc"), + ( + ConversationsAsyncClient, + transports.ConversationsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + ConversationsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationsClient), +) +@mock.patch.object( + ConversationsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationsAsyncClient), +) +def test_conversations_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(ConversationsClient, "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(ConversationsClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (ConversationsClient, transports.ConversationsGrpcTransport, "grpc", "true"), + ( + ConversationsAsyncClient, + transports.ConversationsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (ConversationsClient, transports.ConversationsGrpcTransport, "grpc", "false"), + ( + ConversationsAsyncClient, + transports.ConversationsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + ConversationsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationsClient), +) +@mock.patch.object( + ConversationsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_conversations_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ConversationsClient, transports.ConversationsGrpcTransport, "grpc"), + ( + ConversationsAsyncClient, + transports.ConversationsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_conversations_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ConversationsClient, transports.ConversationsGrpcTransport, "grpc"), + ( + ConversationsAsyncClient, + transports.ConversationsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_conversations_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_conversations_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflow_v2.services.conversations.transports.ConversationsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = ConversationsClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_create_conversation( + transport: str = "grpc", request_type=gcd_conversation.CreateConversationRequest +): + client = ConversationsClient( + 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_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation.Conversation( + name="name_value", + lifecycle_state=gcd_conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=gcd_conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + + response = client.create_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation.CreateConversationRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state + == gcd_conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == gcd_conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +def test_create_conversation_from_dict(): + test_create_conversation(request_type=dict) + + +def test_create_conversation_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation), "__call__" + ) as call: + client.create_conversation() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation.CreateConversationRequest() + + +@pytest.mark.asyncio +async def test_create_conversation_async( + transport: str = "grpc_asyncio", + request_type=gcd_conversation.CreateConversationRequest, +): + client = ConversationsAsyncClient( + 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_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation.Conversation( + name="name_value", + lifecycle_state=gcd_conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=gcd_conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + ) + + response = await client.create_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation.CreateConversationRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state + == gcd_conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == gcd_conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +@pytest.mark.asyncio +async def test_create_conversation_async_from_dict(): + await test_create_conversation_async(request_type=dict) + + +def test_create_conversation_field_headers(): + client = ConversationsClient(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 = gcd_conversation.CreateConversationRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation), "__call__" + ) as call: + call.return_value = gcd_conversation.Conversation() + + client.create_conversation(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_conversation_field_headers_async(): + client = ConversationsAsyncClient(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 = gcd_conversation.CreateConversationRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation.Conversation() + ) + + await client.create_conversation(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_conversation_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation.Conversation() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_conversation( + parent="parent_value", + conversation=gcd_conversation.Conversation(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].conversation == gcd_conversation.Conversation(name="name_value") + + +def test_create_conversation_flattened_error(): + client = ConversationsClient(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_conversation( + gcd_conversation.CreateConversationRequest(), + parent="parent_value", + conversation=gcd_conversation.Conversation(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_conversation_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation.Conversation() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation.Conversation() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_conversation( + parent="parent_value", + conversation=gcd_conversation.Conversation(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].conversation == gcd_conversation.Conversation(name="name_value") + + +@pytest.mark.asyncio +async def test_create_conversation_flattened_error_async(): + client = ConversationsAsyncClient(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_conversation( + gcd_conversation.CreateConversationRequest(), + parent="parent_value", + conversation=gcd_conversation.Conversation(name="name_value"), + ) + + +def test_list_conversations( + transport: str = "grpc", request_type=conversation.ListConversationsRequest +): + client = ConversationsClient( + 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_conversations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListConversationsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_conversations(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListConversationsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListConversationsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_conversations_from_dict(): + test_list_conversations(request_type=dict) + + +def test_list_conversations_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + client.list_conversations() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListConversationsRequest() + + +@pytest.mark.asyncio +async def test_list_conversations_async( + transport: str = "grpc_asyncio", request_type=conversation.ListConversationsRequest +): + client = ConversationsAsyncClient( + 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_conversations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListConversationsResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_conversations(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListConversationsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListConversationsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_conversations_async_from_dict(): + await test_list_conversations_async(request_type=dict) + + +def test_list_conversations_field_headers(): + client = ConversationsClient(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 = conversation.ListConversationsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + call.return_value = conversation.ListConversationsResponse() + + client.list_conversations(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_conversations_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.ListConversationsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListConversationsResponse() + ) + + await client.list_conversations(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_conversations_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListConversationsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_conversations(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_conversations_flattened_error(): + client = ConversationsClient(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_conversations( + conversation.ListConversationsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_conversations_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListConversationsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListConversationsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_conversations(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_conversations_flattened_error_async(): + client = ConversationsAsyncClient(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_conversations( + conversation.ListConversationsRequest(), parent="parent_value", + ) + + +def test_list_conversations_pager(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + conversation.Conversation(), + ], + next_page_token="abc", + ), + conversation.ListConversationsResponse( + conversations=[], next_page_token="def", + ), + conversation.ListConversationsResponse( + conversations=[conversation.Conversation(),], next_page_token="ghi", + ), + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + ], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_conversations(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, conversation.Conversation) for i in results) + + +def test_list_conversations_pages(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + conversation.Conversation(), + ], + next_page_token="abc", + ), + conversation.ListConversationsResponse( + conversations=[], next_page_token="def", + ), + conversation.ListConversationsResponse( + conversations=[conversation.Conversation(),], next_page_token="ghi", + ), + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + ], + ), + RuntimeError, + ) + pages = list(client.list_conversations(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_conversations_async_pager(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + conversation.Conversation(), + ], + next_page_token="abc", + ), + conversation.ListConversationsResponse( + conversations=[], next_page_token="def", + ), + conversation.ListConversationsResponse( + conversations=[conversation.Conversation(),], next_page_token="ghi", + ), + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + ], + ), + RuntimeError, + ) + async_pager = await client.list_conversations(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, conversation.Conversation) for i in responses) + + +@pytest.mark.asyncio +async def test_list_conversations_async_pages(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + conversation.Conversation(), + ], + next_page_token="abc", + ), + conversation.ListConversationsResponse( + conversations=[], next_page_token="def", + ), + conversation.ListConversationsResponse( + conversations=[conversation.Conversation(),], next_page_token="ghi", + ), + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + ], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_conversations(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_conversation( + transport: str = "grpc", request_type=conversation.GetConversationRequest +): + client = ConversationsClient( + 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_conversation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation( + name="name_value", + lifecycle_state=conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + + response = client.get_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.GetConversationRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state == conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +def test_get_conversation_from_dict(): + test_get_conversation(request_type=dict) + + +def test_get_conversation_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_conversation), "__call__") as call: + client.get_conversation() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.GetConversationRequest() + + +@pytest.mark.asyncio +async def test_get_conversation_async( + transport: str = "grpc_asyncio", request_type=conversation.GetConversationRequest +): + client = ConversationsAsyncClient( + 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_conversation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation( + name="name_value", + lifecycle_state=conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + ) + + response = await client.get_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.GetConversationRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state == conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +@pytest.mark.asyncio +async def test_get_conversation_async_from_dict(): + await test_get_conversation_async(request_type=dict) + + +def test_get_conversation_field_headers(): + client = ConversationsClient(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 = conversation.GetConversationRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_conversation), "__call__") as call: + call.return_value = conversation.Conversation() + + client.get_conversation(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_conversation_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.GetConversationRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_conversation), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation() + ) + + await client.get_conversation(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_conversation_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_conversation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_conversation(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_conversation_flattened_error(): + client = ConversationsClient(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_conversation( + conversation.GetConversationRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_conversation_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_conversation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_conversation(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_conversation_flattened_error_async(): + client = ConversationsAsyncClient(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_conversation( + conversation.GetConversationRequest(), name="name_value", + ) + + +def test_complete_conversation( + transport: str = "grpc", request_type=conversation.CompleteConversationRequest +): + client = ConversationsClient( + 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.complete_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation( + name="name_value", + lifecycle_state=conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + + response = client.complete_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CompleteConversationRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state == conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +def test_complete_conversation_from_dict(): + test_complete_conversation(request_type=dict) + + +def test_complete_conversation_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.complete_conversation), "__call__" + ) as call: + client.complete_conversation() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CompleteConversationRequest() + + +@pytest.mark.asyncio +async def test_complete_conversation_async( + transport: str = "grpc_asyncio", + request_type=conversation.CompleteConversationRequest, +): + client = ConversationsAsyncClient( + 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.complete_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation( + name="name_value", + lifecycle_state=conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + ) + + response = await client.complete_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CompleteConversationRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state == conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +@pytest.mark.asyncio +async def test_complete_conversation_async_from_dict(): + await test_complete_conversation_async(request_type=dict) + + +def test_complete_conversation_field_headers(): + client = ConversationsClient(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 = conversation.CompleteConversationRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.complete_conversation), "__call__" + ) as call: + call.return_value = conversation.Conversation() + + client.complete_conversation(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_complete_conversation_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.CompleteConversationRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.complete_conversation), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation() + ) + + await client.complete_conversation(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_complete_conversation_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.complete_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.complete_conversation(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_complete_conversation_flattened_error(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.complete_conversation( + conversation.CompleteConversationRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_complete_conversation_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.complete_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.complete_conversation(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_complete_conversation_flattened_error_async(): + client = ConversationsAsyncClient(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.complete_conversation( + conversation.CompleteConversationRequest(), name="name_value", + ) + + +def test_create_call_matcher( + transport: str = "grpc", request_type=conversation.CreateCallMatcherRequest +): + client = ConversationsClient( + 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_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.CallMatcher( + name="name_value", + to_header="to_header_value", + from_header="from_header_value", + call_id_header="call_id_header_value", + ) + + response = client.create_call_matcher(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CreateCallMatcherRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, conversation.CallMatcher) + + assert response.name == "name_value" + + assert response.to_header == "to_header_value" + + assert response.from_header == "from_header_value" + + assert response.call_id_header == "call_id_header_value" + + +def test_create_call_matcher_from_dict(): + test_create_call_matcher(request_type=dict) + + +def test_create_call_matcher_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_call_matcher), "__call__" + ) as call: + client.create_call_matcher() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CreateCallMatcherRequest() + + +@pytest.mark.asyncio +async def test_create_call_matcher_async( + transport: str = "grpc_asyncio", request_type=conversation.CreateCallMatcherRequest +): + client = ConversationsAsyncClient( + 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_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.CallMatcher( + name="name_value", + to_header="to_header_value", + from_header="from_header_value", + call_id_header="call_id_header_value", + ) + ) + + response = await client.create_call_matcher(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CreateCallMatcherRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.CallMatcher) + + assert response.name == "name_value" + + assert response.to_header == "to_header_value" + + assert response.from_header == "from_header_value" + + assert response.call_id_header == "call_id_header_value" + + +@pytest.mark.asyncio +async def test_create_call_matcher_async_from_dict(): + await test_create_call_matcher_async(request_type=dict) + + +def test_create_call_matcher_field_headers(): + client = ConversationsClient(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 = conversation.CreateCallMatcherRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_call_matcher), "__call__" + ) as call: + call.return_value = conversation.CallMatcher() + + client.create_call_matcher(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_call_matcher_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.CreateCallMatcherRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_call_matcher), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.CallMatcher() + ) + + await client.create_call_matcher(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_call_matcher_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.CallMatcher() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_call_matcher( + parent="parent_value", + call_matcher=conversation.CallMatcher(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].call_matcher == conversation.CallMatcher(name="name_value") + + +def test_create_call_matcher_flattened_error(): + client = ConversationsClient(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_call_matcher( + conversation.CreateCallMatcherRequest(), + parent="parent_value", + call_matcher=conversation.CallMatcher(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_call_matcher_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.CallMatcher() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.CallMatcher() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_call_matcher( + parent="parent_value", + call_matcher=conversation.CallMatcher(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].call_matcher == conversation.CallMatcher(name="name_value") + + +@pytest.mark.asyncio +async def test_create_call_matcher_flattened_error_async(): + client = ConversationsAsyncClient(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_call_matcher( + conversation.CreateCallMatcherRequest(), + parent="parent_value", + call_matcher=conversation.CallMatcher(name="name_value"), + ) + + +def test_list_call_matchers( + transport: str = "grpc", request_type=conversation.ListCallMatchersRequest +): + client = ConversationsClient( + 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_call_matchers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListCallMatchersResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_call_matchers(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListCallMatchersRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListCallMatchersPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_call_matchers_from_dict(): + test_list_call_matchers(request_type=dict) + + +def test_list_call_matchers_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + client.list_call_matchers() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListCallMatchersRequest() + + +@pytest.mark.asyncio +async def test_list_call_matchers_async( + transport: str = "grpc_asyncio", request_type=conversation.ListCallMatchersRequest +): + client = ConversationsAsyncClient( + 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_call_matchers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListCallMatchersResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_call_matchers(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListCallMatchersRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListCallMatchersAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_call_matchers_async_from_dict(): + await test_list_call_matchers_async(request_type=dict) + + +def test_list_call_matchers_field_headers(): + client = ConversationsClient(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 = conversation.ListCallMatchersRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + call.return_value = conversation.ListCallMatchersResponse() + + client.list_call_matchers(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_call_matchers_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.ListCallMatchersRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListCallMatchersResponse() + ) + + await client.list_call_matchers(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_call_matchers_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListCallMatchersResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_call_matchers(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_call_matchers_flattened_error(): + client = ConversationsClient(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_call_matchers( + conversation.ListCallMatchersRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_call_matchers_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListCallMatchersResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListCallMatchersResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_call_matchers(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_call_matchers_flattened_error_async(): + client = ConversationsAsyncClient(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_call_matchers( + conversation.ListCallMatchersRequest(), parent="parent_value", + ) + + +def test_list_call_matchers_pager(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListCallMatchersResponse( + call_matchers=[ + conversation.CallMatcher(), + conversation.CallMatcher(), + conversation.CallMatcher(), + ], + next_page_token="abc", + ), + conversation.ListCallMatchersResponse( + call_matchers=[], next_page_token="def", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(),], next_page_token="ghi", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(), conversation.CallMatcher(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_call_matchers(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, conversation.CallMatcher) for i in results) + + +def test_list_call_matchers_pages(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListCallMatchersResponse( + call_matchers=[ + conversation.CallMatcher(), + conversation.CallMatcher(), + conversation.CallMatcher(), + ], + next_page_token="abc", + ), + conversation.ListCallMatchersResponse( + call_matchers=[], next_page_token="def", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(),], next_page_token="ghi", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(), conversation.CallMatcher(),], + ), + RuntimeError, + ) + pages = list(client.list_call_matchers(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_call_matchers_async_pager(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListCallMatchersResponse( + call_matchers=[ + conversation.CallMatcher(), + conversation.CallMatcher(), + conversation.CallMatcher(), + ], + next_page_token="abc", + ), + conversation.ListCallMatchersResponse( + call_matchers=[], next_page_token="def", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(),], next_page_token="ghi", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(), conversation.CallMatcher(),], + ), + RuntimeError, + ) + async_pager = await client.list_call_matchers(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, conversation.CallMatcher) for i in responses) + + +@pytest.mark.asyncio +async def test_list_call_matchers_async_pages(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListCallMatchersResponse( + call_matchers=[ + conversation.CallMatcher(), + conversation.CallMatcher(), + conversation.CallMatcher(), + ], + next_page_token="abc", + ), + conversation.ListCallMatchersResponse( + call_matchers=[], next_page_token="def", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(),], next_page_token="ghi", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(), conversation.CallMatcher(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_call_matchers(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_call_matcher( + transport: str = "grpc", request_type=conversation.DeleteCallMatcherRequest +): + client = ConversationsClient( + 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_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_call_matcher(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.DeleteCallMatcherRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_call_matcher_from_dict(): + test_delete_call_matcher(request_type=dict) + + +def test_delete_call_matcher_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_call_matcher), "__call__" + ) as call: + client.delete_call_matcher() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.DeleteCallMatcherRequest() + + +@pytest.mark.asyncio +async def test_delete_call_matcher_async( + transport: str = "grpc_asyncio", request_type=conversation.DeleteCallMatcherRequest +): + client = ConversationsAsyncClient( + 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_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_call_matcher(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.DeleteCallMatcherRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_call_matcher_async_from_dict(): + await test_delete_call_matcher_async(request_type=dict) + + +def test_delete_call_matcher_field_headers(): + client = ConversationsClient(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 = conversation.DeleteCallMatcherRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_call_matcher), "__call__" + ) as call: + call.return_value = None + + client.delete_call_matcher(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_call_matcher_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.DeleteCallMatcherRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_call_matcher), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_call_matcher(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_call_matcher_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_call_matcher), "__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_call_matcher(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_call_matcher_flattened_error(): + client = ConversationsClient(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_call_matcher( + conversation.DeleteCallMatcherRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_call_matcher_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_call_matcher), "__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_call_matcher(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_call_matcher_flattened_error_async(): + client = ConversationsAsyncClient(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_call_matcher( + conversation.DeleteCallMatcherRequest(), name="name_value", + ) + + +def test_list_messages( + transport: str = "grpc", request_type=conversation.ListMessagesRequest +): + client = ConversationsClient( + 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_messages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListMessagesResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_messages(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListMessagesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListMessagesPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_messages_from_dict(): + test_list_messages(request_type=dict) + + +def test_list_messages_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + client.list_messages() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListMessagesRequest() + + +@pytest.mark.asyncio +async def test_list_messages_async( + transport: str = "grpc_asyncio", request_type=conversation.ListMessagesRequest +): + client = ConversationsAsyncClient( + 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_messages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListMessagesResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_messages(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListMessagesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListMessagesAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_messages_async_from_dict(): + await test_list_messages_async(request_type=dict) + + +def test_list_messages_field_headers(): + client = ConversationsClient(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 = conversation.ListMessagesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + call.return_value = conversation.ListMessagesResponse() + + client.list_messages(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_messages_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.ListMessagesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListMessagesResponse() + ) + + await client.list_messages(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_messages_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListMessagesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_messages(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_messages_flattened_error(): + client = ConversationsClient(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_messages( + conversation.ListMessagesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_messages_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListMessagesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListMessagesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_messages(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_messages_flattened_error_async(): + client = ConversationsAsyncClient(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_messages( + conversation.ListMessagesRequest(), parent="parent_value", + ) + + +def test_list_messages_pager(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListMessagesResponse( + messages=[ + participant.Message(), + participant.Message(), + participant.Message(), + ], + next_page_token="abc", + ), + conversation.ListMessagesResponse(messages=[], next_page_token="def",), + conversation.ListMessagesResponse( + messages=[participant.Message(),], next_page_token="ghi", + ), + conversation.ListMessagesResponse( + messages=[participant.Message(), participant.Message(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_messages(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, participant.Message) for i in results) + + +def test_list_messages_pages(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListMessagesResponse( + messages=[ + participant.Message(), + participant.Message(), + participant.Message(), + ], + next_page_token="abc", + ), + conversation.ListMessagesResponse(messages=[], next_page_token="def",), + conversation.ListMessagesResponse( + messages=[participant.Message(),], next_page_token="ghi", + ), + conversation.ListMessagesResponse( + messages=[participant.Message(), participant.Message(),], + ), + RuntimeError, + ) + pages = list(client.list_messages(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_messages_async_pager(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_messages), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListMessagesResponse( + messages=[ + participant.Message(), + participant.Message(), + participant.Message(), + ], + next_page_token="abc", + ), + conversation.ListMessagesResponse(messages=[], next_page_token="def",), + conversation.ListMessagesResponse( + messages=[participant.Message(),], next_page_token="ghi", + ), + conversation.ListMessagesResponse( + messages=[participant.Message(), participant.Message(),], + ), + RuntimeError, + ) + async_pager = await client.list_messages(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, participant.Message) for i in responses) + + +@pytest.mark.asyncio +async def test_list_messages_async_pages(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_messages), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListMessagesResponse( + messages=[ + participant.Message(), + participant.Message(), + participant.Message(), + ], + next_page_token="abc", + ), + conversation.ListMessagesResponse(messages=[], next_page_token="def",), + conversation.ListMessagesResponse( + messages=[participant.Message(),], next_page_token="ghi", + ), + conversation.ListMessagesResponse( + messages=[participant.Message(), participant.Message(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_messages(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.ConversationsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.ConversationsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.ConversationsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.ConversationsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = ConversationsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.ConversationsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.ConversationsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationsGrpcTransport, + transports.ConversationsGrpcAsyncIOTransport, + ], +) +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 = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.ConversationsGrpcTransport,) + + +def test_conversations_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.ConversationsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_conversations_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflow_v2.services.conversations.transports.ConversationsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.ConversationsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "create_conversation", + "list_conversations", + "get_conversation", + "complete_conversation", + "create_call_matcher", + "list_call_matchers", + "delete_call_matcher", + "list_messages", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_conversations_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.dialogflow_v2.services.conversations.transports.ConversationsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ConversationsTransport( + 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_conversations_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.dialogflow_v2.services.conversations.transports.ConversationsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ConversationsTransport() + adc.assert_called_once() + + +def test_conversations_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) + ConversationsClient() + 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_conversations_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.ConversationsGrpcTransport( + 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", + ) + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationsGrpcTransport, + transports.ConversationsGrpcAsyncIOTransport, + ], +) +def test_conversations_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_conversations_host_no_port(): + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_conversations_host_with_port(): + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_conversations_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ConversationsGrpcTransport( + 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_conversations_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ConversationsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationsGrpcTransport, + transports.ConversationsGrpcAsyncIOTransport, + ], +) +def test_conversations_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationsGrpcTransport, + transports.ConversationsGrpcAsyncIOTransport, + ], +) +def test_conversations_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_call_matcher_path(): + project = "squid" + conversation = "clam" + call_matcher = "whelk" + + expected = "projects/{project}/conversations/{conversation}/callMatchers/{call_matcher}".format( + project=project, conversation=conversation, call_matcher=call_matcher, + ) + actual = ConversationsClient.call_matcher_path(project, conversation, call_matcher) + assert expected == actual + + +def test_parse_call_matcher_path(): + expected = { + "project": "octopus", + "conversation": "oyster", + "call_matcher": "nudibranch", + } + path = ConversationsClient.call_matcher_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_call_matcher_path(path) + assert expected == actual + + +def test_conversation_path(): + project = "cuttlefish" + conversation = "mussel" + + expected = "projects/{project}/conversations/{conversation}".format( + project=project, conversation=conversation, + ) + actual = ConversationsClient.conversation_path(project, conversation) + assert expected == actual + + +def test_parse_conversation_path(): + expected = { + "project": "winkle", + "conversation": "nautilus", + } + path = ConversationsClient.conversation_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_conversation_path(path) + assert expected == actual + + +def test_conversation_profile_path(): + project = "scallop" + conversation_profile = "abalone" + + expected = "projects/{project}/conversationProfiles/{conversation_profile}".format( + project=project, conversation_profile=conversation_profile, + ) + actual = ConversationsClient.conversation_profile_path( + project, conversation_profile + ) + assert expected == actual + + +def test_parse_conversation_profile_path(): + expected = { + "project": "squid", + "conversation_profile": "clam", + } + path = ConversationsClient.conversation_profile_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_conversation_profile_path(path) + assert expected == actual + + +def test_message_path(): + project = "whelk" + conversation = "octopus" + message = "oyster" + + expected = "projects/{project}/conversations/{conversation}/messages/{message}".format( + project=project, conversation=conversation, message=message, + ) + actual = ConversationsClient.message_path(project, conversation, message) + assert expected == actual + + +def test_parse_message_path(): + expected = { + "project": "nudibranch", + "conversation": "cuttlefish", + "message": "mussel", + } + path = ConversationsClient.message_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_message_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "winkle" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = ConversationsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nautilus", + } + path = ConversationsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "scallop" + + expected = "folders/{folder}".format(folder=folder,) + actual = ConversationsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "abalone", + } + path = ConversationsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "squid" + + expected = "organizations/{organization}".format(organization=organization,) + actual = ConversationsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "clam", + } + path = ConversationsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "whelk" + + expected = "projects/{project}".format(project=project,) + actual = ConversationsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "octopus", + } + path = ConversationsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.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 = ConversationsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + } + path = ConversationsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.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.ConversationsTransport, "_prep_wrapped_messages" + ) as prep: + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.ConversationsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = ConversationsClient.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/dialogflow_v2/test_documents.py b/tests/unit/gapic/dialogflow_v2/test_documents.py new file mode 100644 index 000000000..e61b3a1e7 --- /dev/null +++ b/tests/unit/gapic/dialogflow_v2/test_documents.py @@ -0,0 +1,2398 @@ +# -*- 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.dialogflow_v2.services.documents import DocumentsAsyncClient +from google.cloud.dialogflow_v2.services.documents import DocumentsClient +from google.cloud.dialogflow_v2.services.documents import pagers +from google.cloud.dialogflow_v2.services.documents import transports +from google.cloud.dialogflow_v2.types import document +from google.cloud.dialogflow_v2.types import document as gcd_document +from google.longrunning import operations_pb2 +from google.oauth2 import service_account +from google.protobuf import any_pb2 as gp_any # type: ignore +from google.protobuf import field_mask_pb2 as field_mask # type: ignore +from google.protobuf import timestamp_pb2 as timestamp # type: ignore +from google.rpc import status_pb2 as status # 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 DocumentsClient._get_default_mtls_endpoint(None) is None + assert DocumentsClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + assert ( + DocumentsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + DocumentsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + DocumentsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert DocumentsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [DocumentsClient, DocumentsAsyncClient,]) +def test_documents_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [DocumentsClient, DocumentsAsyncClient,]) +def test_documents_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 + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_documents_client_get_transport_class(): + transport = DocumentsClient.get_transport_class() + available_transports = [ + transports.DocumentsGrpcTransport, + ] + assert transport in available_transports + + transport = DocumentsClient.get_transport_class("grpc") + assert transport == transports.DocumentsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (DocumentsClient, transports.DocumentsGrpcTransport, "grpc"), + ( + DocumentsAsyncClient, + transports.DocumentsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + DocumentsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(DocumentsClient) +) +@mock.patch.object( + DocumentsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(DocumentsAsyncClient), +) +def test_documents_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(DocumentsClient, "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(DocumentsClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (DocumentsClient, transports.DocumentsGrpcTransport, "grpc", "true"), + ( + DocumentsAsyncClient, + transports.DocumentsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (DocumentsClient, transports.DocumentsGrpcTransport, "grpc", "false"), + ( + DocumentsAsyncClient, + transports.DocumentsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + DocumentsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(DocumentsClient) +) +@mock.patch.object( + DocumentsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(DocumentsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_documents_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (DocumentsClient, transports.DocumentsGrpcTransport, "grpc"), + ( + DocumentsAsyncClient, + transports.DocumentsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_documents_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (DocumentsClient, transports.DocumentsGrpcTransport, "grpc"), + ( + DocumentsAsyncClient, + transports.DocumentsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_documents_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_documents_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflow_v2.services.documents.transports.DocumentsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = DocumentsClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_documents( + transport: str = "grpc", request_type=document.ListDocumentsRequest +): + client = DocumentsClient( + 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_documents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = document.ListDocumentsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_documents(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == document.ListDocumentsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListDocumentsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_documents_from_dict(): + test_list_documents(request_type=dict) + + +def test_list_documents_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_documents), "__call__") as call: + client.list_documents() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == document.ListDocumentsRequest() + + +@pytest.mark.asyncio +async def test_list_documents_async( + transport: str = "grpc_asyncio", request_type=document.ListDocumentsRequest +): + client = DocumentsAsyncClient( + 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_documents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + document.ListDocumentsResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_documents(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == document.ListDocumentsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListDocumentsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_documents_async_from_dict(): + await test_list_documents_async(request_type=dict) + + +def test_list_documents_field_headers(): + client = DocumentsClient(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 = document.ListDocumentsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_documents), "__call__") as call: + call.return_value = document.ListDocumentsResponse() + + client.list_documents(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_documents_field_headers_async(): + client = DocumentsAsyncClient(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 = document.ListDocumentsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_documents), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + document.ListDocumentsResponse() + ) + + await client.list_documents(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_documents_flattened(): + client = DocumentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_documents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = document.ListDocumentsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_documents(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_documents_flattened_error(): + client = DocumentsClient(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_documents( + document.ListDocumentsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_documents_flattened_async(): + client = DocumentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_documents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = document.ListDocumentsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + document.ListDocumentsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_documents(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_documents_flattened_error_async(): + client = DocumentsAsyncClient(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_documents( + document.ListDocumentsRequest(), parent="parent_value", + ) + + +def test_list_documents_pager(): + client = DocumentsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_documents), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + document.ListDocumentsResponse( + documents=[ + document.Document(), + document.Document(), + document.Document(), + ], + next_page_token="abc", + ), + document.ListDocumentsResponse(documents=[], next_page_token="def",), + document.ListDocumentsResponse( + documents=[document.Document(),], next_page_token="ghi", + ), + document.ListDocumentsResponse( + documents=[document.Document(), document.Document(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_documents(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, document.Document) for i in results) + + +def test_list_documents_pages(): + client = DocumentsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_documents), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + document.ListDocumentsResponse( + documents=[ + document.Document(), + document.Document(), + document.Document(), + ], + next_page_token="abc", + ), + document.ListDocumentsResponse(documents=[], next_page_token="def",), + document.ListDocumentsResponse( + documents=[document.Document(),], next_page_token="ghi", + ), + document.ListDocumentsResponse( + documents=[document.Document(), document.Document(),], + ), + RuntimeError, + ) + pages = list(client.list_documents(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_documents_async_pager(): + client = DocumentsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_documents), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + document.ListDocumentsResponse( + documents=[ + document.Document(), + document.Document(), + document.Document(), + ], + next_page_token="abc", + ), + document.ListDocumentsResponse(documents=[], next_page_token="def",), + document.ListDocumentsResponse( + documents=[document.Document(),], next_page_token="ghi", + ), + document.ListDocumentsResponse( + documents=[document.Document(), document.Document(),], + ), + RuntimeError, + ) + async_pager = await client.list_documents(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, document.Document) for i in responses) + + +@pytest.mark.asyncio +async def test_list_documents_async_pages(): + client = DocumentsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_documents), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + document.ListDocumentsResponse( + documents=[ + document.Document(), + document.Document(), + document.Document(), + ], + next_page_token="abc", + ), + document.ListDocumentsResponse(documents=[], next_page_token="def",), + document.ListDocumentsResponse( + documents=[document.Document(),], next_page_token="ghi", + ), + document.ListDocumentsResponse( + documents=[document.Document(), document.Document(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_documents(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_document( + transport: str = "grpc", request_type=document.GetDocumentRequest +): + client = DocumentsClient( + 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_document), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = document.Document( + name="name_value", + display_name="display_name_value", + mime_type="mime_type_value", + knowledge_types=[document.Document.KnowledgeType.FAQ], + enable_auto_reload=True, + content_uri="content_uri_value", + ) + + response = client.get_document(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == document.GetDocumentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, document.Document) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.mime_type == "mime_type_value" + + assert response.knowledge_types == [document.Document.KnowledgeType.FAQ] + + assert response.enable_auto_reload is True + + +def test_get_document_from_dict(): + test_get_document(request_type=dict) + + +def test_get_document_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_document), "__call__") as call: + client.get_document() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == document.GetDocumentRequest() + + +@pytest.mark.asyncio +async def test_get_document_async( + transport: str = "grpc_asyncio", request_type=document.GetDocumentRequest +): + client = DocumentsAsyncClient( + 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_document), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + document.Document( + name="name_value", + display_name="display_name_value", + mime_type="mime_type_value", + knowledge_types=[document.Document.KnowledgeType.FAQ], + enable_auto_reload=True, + ) + ) + + response = await client.get_document(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == document.GetDocumentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, document.Document) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.mime_type == "mime_type_value" + + assert response.knowledge_types == [document.Document.KnowledgeType.FAQ] + + assert response.enable_auto_reload is True + + +@pytest.mark.asyncio +async def test_get_document_async_from_dict(): + await test_get_document_async(request_type=dict) + + +def test_get_document_field_headers(): + client = DocumentsClient(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 = document.GetDocumentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_document), "__call__") as call: + call.return_value = document.Document() + + client.get_document(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_document_field_headers_async(): + client = DocumentsAsyncClient(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 = document.GetDocumentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_document), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(document.Document()) + + await client.get_document(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_document_flattened(): + client = DocumentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_document), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = document.Document() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_document(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_document_flattened_error(): + client = DocumentsClient(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_document( + document.GetDocumentRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_document_flattened_async(): + client = DocumentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_document), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = document.Document() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(document.Document()) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_document(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_document_flattened_error_async(): + client = DocumentsAsyncClient(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_document( + document.GetDocumentRequest(), name="name_value", + ) + + +def test_create_document( + transport: str = "grpc", request_type=gcd_document.CreateDocumentRequest +): + client = DocumentsClient( + 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_document), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.create_document(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_document.CreateDocumentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_create_document_from_dict(): + test_create_document(request_type=dict) + + +def test_create_document_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_document), "__call__") as call: + client.create_document() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_document.CreateDocumentRequest() + + +@pytest.mark.asyncio +async def test_create_document_async( + transport: str = "grpc_asyncio", request_type=gcd_document.CreateDocumentRequest +): + client = DocumentsAsyncClient( + 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_document), "__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_document(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_document.CreateDocumentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_create_document_async_from_dict(): + await test_create_document_async(request_type=dict) + + +def test_create_document_field_headers(): + client = DocumentsClient(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 = gcd_document.CreateDocumentRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_document), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.create_document(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_document_field_headers_async(): + client = DocumentsAsyncClient(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 = gcd_document.CreateDocumentRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_document), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.create_document(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_document_flattened(): + client = DocumentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_document), "__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_document( + parent="parent_value", document=gcd_document.Document(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].document == gcd_document.Document(name="name_value") + + +def test_create_document_flattened_error(): + client = DocumentsClient(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_document( + gcd_document.CreateDocumentRequest(), + parent="parent_value", + document=gcd_document.Document(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_document_flattened_async(): + client = DocumentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_document), "__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_document( + parent="parent_value", document=gcd_document.Document(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].document == gcd_document.Document(name="name_value") + + +@pytest.mark.asyncio +async def test_create_document_flattened_error_async(): + client = DocumentsAsyncClient(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_document( + gcd_document.CreateDocumentRequest(), + parent="parent_value", + document=gcd_document.Document(name="name_value"), + ) + + +def test_delete_document( + transport: str = "grpc", request_type=document.DeleteDocumentRequest +): + client = DocumentsClient( + 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_document), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.delete_document(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == document.DeleteDocumentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_delete_document_from_dict(): + test_delete_document(request_type=dict) + + +def test_delete_document_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_document), "__call__") as call: + client.delete_document() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == document.DeleteDocumentRequest() + + +@pytest.mark.asyncio +async def test_delete_document_async( + transport: str = "grpc_asyncio", request_type=document.DeleteDocumentRequest +): + client = DocumentsAsyncClient( + 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_document), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + + response = await client.delete_document(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == document.DeleteDocumentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_delete_document_async_from_dict(): + await test_delete_document_async(request_type=dict) + + +def test_delete_document_field_headers(): + client = DocumentsClient(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 = document.DeleteDocumentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_document), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.delete_document(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_document_field_headers_async(): + client = DocumentsAsyncClient(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 = document.DeleteDocumentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_document), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.delete_document(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_document_flattened(): + client = DocumentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_document), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.delete_document(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_document_flattened_error(): + client = DocumentsClient(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_document( + document.DeleteDocumentRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_document_flattened_async(): + client = DocumentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_document), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/op") + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/spam") + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.delete_document(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_document_flattened_error_async(): + client = DocumentsAsyncClient(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_document( + document.DeleteDocumentRequest(), name="name_value", + ) + + +def test_update_document( + transport: str = "grpc", request_type=gcd_document.UpdateDocumentRequest +): + client = DocumentsClient( + 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_document), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.update_document(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_document.UpdateDocumentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_update_document_from_dict(): + test_update_document(request_type=dict) + + +def test_update_document_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_document), "__call__") as call: + client.update_document() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_document.UpdateDocumentRequest() + + +@pytest.mark.asyncio +async def test_update_document_async( + transport: str = "grpc_asyncio", request_type=gcd_document.UpdateDocumentRequest +): + client = DocumentsAsyncClient( + 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_document), "__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_document(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_document.UpdateDocumentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_update_document_async_from_dict(): + await test_update_document_async(request_type=dict) + + +def test_update_document_field_headers(): + client = DocumentsClient(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 = gcd_document.UpdateDocumentRequest() + request.document.name = "document.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_document), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.update_document(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", "document.name=document.name/value",) in kw[ + "metadata" + ] + + +@pytest.mark.asyncio +async def test_update_document_field_headers_async(): + client = DocumentsAsyncClient(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 = gcd_document.UpdateDocumentRequest() + request.document.name = "document.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_document), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.update_document(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", "document.name=document.name/value",) in kw[ + "metadata" + ] + + +def test_update_document_flattened(): + client = DocumentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_document), "__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_document( + document=gcd_document.Document(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].document == gcd_document.Document(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_document_flattened_error(): + client = DocumentsClient(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_document( + gcd_document.UpdateDocumentRequest(), + document=gcd_document.Document(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_document_flattened_async(): + client = DocumentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_document), "__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_document( + document=gcd_document.Document(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].document == gcd_document.Document(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_document_flattened_error_async(): + client = DocumentsAsyncClient(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_document( + gcd_document.UpdateDocumentRequest(), + document=gcd_document.Document(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_reload_document( + transport: str = "grpc", request_type=document.ReloadDocumentRequest +): + client = DocumentsClient( + 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.reload_document), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.reload_document(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == document.ReloadDocumentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_reload_document_from_dict(): + test_reload_document(request_type=dict) + + +def test_reload_document_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.reload_document), "__call__") as call: + client.reload_document() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == document.ReloadDocumentRequest() + + +@pytest.mark.asyncio +async def test_reload_document_async( + transport: str = "grpc_asyncio", request_type=document.ReloadDocumentRequest +): + client = DocumentsAsyncClient( + 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.reload_document), "__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.reload_document(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == document.ReloadDocumentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_reload_document_async_from_dict(): + await test_reload_document_async(request_type=dict) + + +def test_reload_document_field_headers(): + client = DocumentsClient(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 = document.ReloadDocumentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.reload_document), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.reload_document(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_reload_document_field_headers_async(): + client = DocumentsAsyncClient(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 = document.ReloadDocumentRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.reload_document), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.reload_document(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_reload_document_flattened(): + client = DocumentsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.reload_document), "__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.reload_document( + name="name_value", content_uri="content_uri_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].content_uri == "content_uri_value" + + +def test_reload_document_flattened_error(): + client = DocumentsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.reload_document( + document.ReloadDocumentRequest(), + name="name_value", + content_uri="content_uri_value", + ) + + +@pytest.mark.asyncio +async def test_reload_document_flattened_async(): + client = DocumentsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.reload_document), "__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.reload_document( + name="name_value", content_uri="content_uri_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0].name == "name_value" + + assert args[0].content_uri == "content_uri_value" + + +@pytest.mark.asyncio +async def test_reload_document_flattened_error_async(): + client = DocumentsAsyncClient(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.reload_document( + document.ReloadDocumentRequest(), + name="name_value", + content_uri="content_uri_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.DocumentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.DocumentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = DocumentsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.DocumentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = DocumentsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.DocumentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = DocumentsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.DocumentsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.DocumentsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [transports.DocumentsGrpcTransport, transports.DocumentsGrpcAsyncIOTransport,], +) +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 = DocumentsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.DocumentsGrpcTransport,) + + +def test_documents_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.DocumentsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_documents_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflow_v2.services.documents.transports.DocumentsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.DocumentsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_documents", + "get_document", + "create_document", + "delete_document", + "update_document", + "reload_document", + ) + 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_documents_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.dialogflow_v2.services.documents.transports.DocumentsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.DocumentsTransport( + 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_documents_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.dialogflow_v2.services.documents.transports.DocumentsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.DocumentsTransport() + adc.assert_called_once() + + +def test_documents_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) + DocumentsClient() + 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_documents_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.DocumentsGrpcTransport( + 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", + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.DocumentsGrpcTransport, transports.DocumentsGrpcAsyncIOTransport], +) +def test_documents_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_documents_host_no_port(): + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_documents_host_with_port(): + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_documents_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.DocumentsGrpcTransport( + 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_documents_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.DocumentsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.DocumentsGrpcTransport, transports.DocumentsGrpcAsyncIOTransport], +) +def test_documents_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.DocumentsGrpcTransport, transports.DocumentsGrpcAsyncIOTransport], +) +def test_documents_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_documents_grpc_lro_client(): + client = DocumentsClient( + 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_documents_grpc_lro_async_client(): + client = DocumentsAsyncClient( + 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_document_path(): + project = "squid" + knowledge_base = "clam" + document = "whelk" + + expected = "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, knowledge_base=knowledge_base, document=document, + ) + actual = DocumentsClient.document_path(project, knowledge_base, document) + assert expected == actual + + +def test_parse_document_path(): + expected = { + "project": "octopus", + "knowledge_base": "oyster", + "document": "nudibranch", + } + path = DocumentsClient.document_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentsClient.parse_document_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "cuttlefish" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = DocumentsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "mussel", + } + path = DocumentsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "winkle" + + expected = "folders/{folder}".format(folder=folder,) + actual = DocumentsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "nautilus", + } + path = DocumentsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "scallop" + + expected = "organizations/{organization}".format(organization=organization,) + actual = DocumentsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "abalone", + } + path = DocumentsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "squid" + + expected = "projects/{project}".format(project=project,) + actual = DocumentsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "clam", + } + path = DocumentsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentsClient.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 = DocumentsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "oyster", + "location": "nudibranch", + } + path = DocumentsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = DocumentsClient.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.DocumentsTransport, "_prep_wrapped_messages" + ) as prep: + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.DocumentsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = DocumentsClient.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/dialogflow_v2/test_entity_types.py b/tests/unit/gapic/dialogflow_v2/test_entity_types.py index cfa58bb29..a35603f06 100644 --- a/tests/unit/gapic/dialogflow_v2/test_entity_types.py +++ b/tests/unit/gapic/dialogflow_v2/test_entity_types.py @@ -88,7 +88,22 @@ def test__get_default_mtls_endpoint(): assert EntityTypesClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [EntityTypesClient, EntityTypesAsyncClient]) +@pytest.mark.parametrize("client_class", [EntityTypesClient, EntityTypesAsyncClient,]) +def test_entity_types_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@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( @@ -97,16 +112,21 @@ def test_entity_types_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_entity_types_client_get_transport_class(): transport = EntityTypesClient.get_transport_class() - assert transport == transports.EntityTypesGrpcTransport + available_transports = [ + transports.EntityTypesGrpcTransport, + ] + assert transport in available_transports transport = EntityTypesClient.get_transport_class("grpc") assert transport == transports.EntityTypesGrpcTransport @@ -155,7 +175,7 @@ def test_entity_types_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -171,7 +191,7 @@ def test_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -187,7 +207,7 @@ def test_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -215,7 +235,7 @@ def test_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -264,29 +284,25 @@ def test_entity_types_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -295,66 +311,53 @@ def test_entity_types_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -380,7 +383,7 @@ def test_entity_types_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -410,7 +413,7 @@ def test_entity_types_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -427,7 +430,7 @@ def test_entity_types_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -472,6 +475,24 @@ def test_list_entity_types_from_dict(): test_list_entity_types(request_type=dict) +def test_list_entity_types_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_entity_types), "__call__" + ) as call: + client.list_entity_types() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.ListEntityTypesRequest() + + @pytest.mark.asyncio async def test_list_entity_types_async( transport: str = "grpc_asyncio", request_type=entity_type.ListEntityTypesRequest @@ -858,6 +879,22 @@ def test_get_entity_type_from_dict(): test_get_entity_type(request_type=dict) +def test_get_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: + client.get_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.GetEntityTypeRequest() + + @pytest.mark.asyncio async def test_get_entity_type_async( transport: str = "grpc_asyncio", request_type=entity_type.GetEntityTypeRequest @@ -1097,6 +1134,24 @@ def test_create_entity_type_from_dict(): test_create_entity_type(request_type=dict) +def test_create_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.create_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_entity_type.CreateEntityTypeRequest() + + @pytest.mark.asyncio async def test_create_entity_type_async( transport: str = "grpc_asyncio", @@ -1357,6 +1412,24 @@ def test_update_entity_type_from_dict(): test_update_entity_type(request_type=dict) +def test_update_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.update_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_entity_type.UpdateEntityTypeRequest() + + @pytest.mark.asyncio async def test_update_entity_type_async( transport: str = "grpc_asyncio", @@ -1593,6 +1666,24 @@ def test_delete_entity_type_from_dict(): test_delete_entity_type(request_type=dict) +def test_delete_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.delete_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.DeleteEntityTypeRequest() + + @pytest.mark.asyncio async def test_delete_entity_type_async( transport: str = "grpc_asyncio", request_type=entity_type.DeleteEntityTypeRequest @@ -1785,6 +1876,24 @@ def test_batch_update_entity_types_from_dict(): test_batch_update_entity_types(request_type=dict) +def test_batch_update_entity_types_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_update_entity_types), "__call__" + ) as call: + client.batch_update_entity_types() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.BatchUpdateEntityTypesRequest() + + @pytest.mark.asyncio async def test_batch_update_entity_types_async( transport: str = "grpc_asyncio", @@ -1913,6 +2022,24 @@ def test_batch_delete_entity_types_from_dict(): test_batch_delete_entity_types(request_type=dict) +def test_batch_delete_entity_types_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_delete_entity_types), "__call__" + ) as call: + client.batch_delete_entity_types() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.BatchDeleteEntityTypesRequest() + + @pytest.mark.asyncio async def test_batch_delete_entity_types_async( transport: str = "grpc_asyncio", @@ -2124,6 +2251,24 @@ def test_batch_create_entities_from_dict(): test_batch_create_entities(request_type=dict) +def test_batch_create_entities_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_create_entities), "__call__" + ) as call: + client.batch_create_entities() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.BatchCreateEntitiesRequest() + + @pytest.mark.asyncio async def test_batch_create_entities_async( transport: str = "grpc_asyncio", request_type=entity_type.BatchCreateEntitiesRequest @@ -2344,6 +2489,24 @@ def test_batch_update_entities_from_dict(): test_batch_update_entities(request_type=dict) +def test_batch_update_entities_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_update_entities), "__call__" + ) as call: + client.batch_update_entities() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.BatchUpdateEntitiesRequest() + + @pytest.mark.asyncio async def test_batch_update_entities_async( transport: str = "grpc_asyncio", request_type=entity_type.BatchUpdateEntitiesRequest @@ -2564,6 +2727,24 @@ def test_batch_delete_entities_from_dict(): test_batch_delete_entities(request_type=dict) +def test_batch_delete_entities_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_delete_entities), "__call__" + ) as call: + client.batch_delete_entities() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.BatchDeleteEntitiesRequest() + + @pytest.mark.asyncio async def test_batch_delete_entities_async( transport: str = "grpc_asyncio", request_type=entity_type.BatchDeleteEntitiesRequest @@ -2806,7 +2987,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], + [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -2929,6 +3110,51 @@ def test_entity_types_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], +) +def test_entity_types_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_entity_types_host_no_port(): client = EntityTypesClient( credentials=credentials.AnonymousCredentials(), @@ -2950,7 +3176,7 @@ def test_entity_types_host_with_port(): def test_entity_types_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.EntityTypesGrpcTransport( @@ -2962,7 +3188,7 @@ def test_entity_types_grpc_transport_channel(): def test_entity_types_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.EntityTypesGrpcAsyncIOTransport( @@ -2973,6 +3199,8 @@ def test_entity_types_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], @@ -2982,7 +3210,7 @@ def test_entity_types_transport_channel_mtls_with_client_cert_source(transport_c "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -3023,6 +3251,8 @@ def test_entity_types_transport_channel_mtls_with_client_cert_source(transport_c assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], @@ -3035,7 +3265,7 @@ def test_entity_types_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2/test_environments.py b/tests/unit/gapic/dialogflow_v2/test_environments.py index 5601260c4..df149b933 100644 --- a/tests/unit/gapic/dialogflow_v2/test_environments.py +++ b/tests/unit/gapic/dialogflow_v2/test_environments.py @@ -81,7 +81,22 @@ def test__get_default_mtls_endpoint(): assert EnvironmentsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [EnvironmentsClient, EnvironmentsAsyncClient]) +@pytest.mark.parametrize("client_class", [EnvironmentsClient, EnvironmentsAsyncClient,]) +def test_environments_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [EnvironmentsClient, EnvironmentsAsyncClient,]) def test_environments_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -90,16 +105,21 @@ def test_environments_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_environments_client_get_transport_class(): transport = EnvironmentsClient.get_transport_class() - assert transport == transports.EnvironmentsGrpcTransport + available_transports = [ + transports.EnvironmentsGrpcTransport, + ] + assert transport in available_transports transport = EnvironmentsClient.get_transport_class("grpc") assert transport == transports.EnvironmentsGrpcTransport @@ -148,7 +168,7 @@ def test_environments_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -164,7 +184,7 @@ def test_environments_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -180,7 +200,7 @@ def test_environments_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -208,7 +228,7 @@ def test_environments_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -257,29 +277,25 @@ def test_environments_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -288,66 +304,53 @@ def test_environments_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -373,7 +376,7 @@ def test_environments_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -403,7 +406,7 @@ def test_environments_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -420,7 +423,7 @@ def test_environments_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -465,6 +468,24 @@ def test_list_environments_from_dict(): test_list_environments(request_type=dict) +def test_list_environments_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EnvironmentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_environments), "__call__" + ) as call: + client.list_environments() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == environment.ListEnvironmentsRequest() + + @pytest.mark.asyncio async def test_list_environments_async( transport: str = "grpc_asyncio", request_type=environment.ListEnvironmentsRequest @@ -772,7 +793,10 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], + [ + transports.EnvironmentsGrpcTransport, + transports.EnvironmentsGrpcAsyncIOTransport, + ], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -879,6 +903,51 @@ def test_environments_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], +) +def test_environments_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_environments_host_no_port(): client = EnvironmentsClient( credentials=credentials.AnonymousCredentials(), @@ -900,7 +969,7 @@ def test_environments_host_with_port(): def test_environments_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.EnvironmentsGrpcTransport( @@ -912,7 +981,7 @@ def test_environments_grpc_transport_channel(): def test_environments_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.EnvironmentsGrpcAsyncIOTransport( @@ -923,6 +992,8 @@ def test_environments_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], @@ -932,7 +1003,7 @@ def test_environments_transport_channel_mtls_with_client_cert_source(transport_c "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -973,6 +1044,8 @@ def test_environments_transport_channel_mtls_with_client_cert_source(transport_c assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], @@ -985,7 +1058,7 @@ def test_environments_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2/test_intents.py b/tests/unit/gapic/dialogflow_v2/test_intents.py index ac7d9d95f..0cbed51cb 100644 --- a/tests/unit/gapic/dialogflow_v2/test_intents.py +++ b/tests/unit/gapic/dialogflow_v2/test_intents.py @@ -86,7 +86,22 @@ def test__get_default_mtls_endpoint(): assert IntentsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [IntentsClient, IntentsAsyncClient]) +@pytest.mark.parametrize("client_class", [IntentsClient, IntentsAsyncClient,]) +def test_intents_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [IntentsClient, IntentsAsyncClient,]) def test_intents_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -95,16 +110,21 @@ def test_intents_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_intents_client_get_transport_class(): transport = IntentsClient.get_transport_class() - assert transport == transports.IntentsGrpcTransport + available_transports = [ + transports.IntentsGrpcTransport, + ] + assert transport in available_transports transport = IntentsClient.get_transport_class("grpc") assert transport == transports.IntentsGrpcTransport @@ -145,7 +165,7 @@ def test_intents_client_client_options(client_class, transport_class, transport_ credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -161,7 +181,7 @@ def test_intents_client_client_options(client_class, transport_class, transport_ credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -177,7 +197,7 @@ def test_intents_client_client_options(client_class, transport_class, transport_ credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -205,7 +225,7 @@ def test_intents_client_client_options(client_class, transport_class, transport_ credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -252,29 +272,25 @@ def test_intents_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -283,66 +299,53 @@ def test_intents_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -364,7 +367,7 @@ def test_intents_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -390,7 +393,7 @@ def test_intents_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -407,7 +410,7 @@ def test_intents_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -448,6 +451,22 @@ def test_list_intents_from_dict(): test_list_intents(request_type=dict) +def test_list_intents_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: + client.list_intents() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.ListIntentsRequest() + + @pytest.mark.asyncio async def test_list_intents_async( transport: str = "grpc_asyncio", request_type=intent.ListIntentsRequest @@ -749,6 +768,8 @@ def test_get_intent(transport: str = "grpc", request_type=intent.GetIntentReques priority=898, is_fallback=True, ml_disabled=True, + live_agent_handoff=True, + end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], action="action_value", @@ -782,6 +803,10 @@ def test_get_intent(transport: str = "grpc", request_type=intent.GetIntentReques assert response.ml_disabled is True + assert response.live_agent_handoff is True + + assert response.end_interaction is True + assert response.input_context_names == ["input_context_names_value"] assert response.events == ["events_value"] @@ -803,6 +828,22 @@ def test_get_intent_from_dict(): test_get_intent(request_type=dict) +def test_get_intent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: + client.get_intent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.GetIntentRequest() + + @pytest.mark.asyncio async def test_get_intent_async( transport: str = "grpc_asyncio", request_type=intent.GetIntentRequest @@ -826,6 +867,8 @@ async def test_get_intent_async( priority=898, is_fallback=True, ml_disabled=True, + live_agent_handoff=True, + end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], action="action_value", @@ -859,6 +902,10 @@ async def test_get_intent_async( assert response.ml_disabled is True + assert response.live_agent_handoff is True + + assert response.end_interaction is True + assert response.input_context_names == ["input_context_names_value"] assert response.events == ["events_value"] @@ -1028,6 +1075,8 @@ def test_create_intent( priority=898, is_fallback=True, ml_disabled=True, + live_agent_handoff=True, + end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], action="action_value", @@ -1063,6 +1112,10 @@ def test_create_intent( assert response.ml_disabled is True + assert response.live_agent_handoff is True + + assert response.end_interaction is True + assert response.input_context_names == ["input_context_names_value"] assert response.events == ["events_value"] @@ -1084,6 +1137,22 @@ def test_create_intent_from_dict(): test_create_intent(request_type=dict) +def test_create_intent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: + client.create_intent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_intent.CreateIntentRequest() + + @pytest.mark.asyncio async def test_create_intent_async( transport: str = "grpc_asyncio", request_type=gcd_intent.CreateIntentRequest @@ -1107,6 +1176,8 @@ async def test_create_intent_async( priority=898, is_fallback=True, ml_disabled=True, + live_agent_handoff=True, + end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], action="action_value", @@ -1144,6 +1215,10 @@ async def test_create_intent_async( assert response.ml_disabled is True + assert response.live_agent_handoff is True + + assert response.end_interaction is True + assert response.input_context_names == ["input_context_names_value"] assert response.events == ["events_value"] @@ -1323,6 +1398,8 @@ def test_update_intent( priority=898, is_fallback=True, ml_disabled=True, + live_agent_handoff=True, + end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], action="action_value", @@ -1358,6 +1435,10 @@ def test_update_intent( assert response.ml_disabled is True + assert response.live_agent_handoff is True + + assert response.end_interaction is True + assert response.input_context_names == ["input_context_names_value"] assert response.events == ["events_value"] @@ -1379,6 +1460,22 @@ def test_update_intent_from_dict(): test_update_intent(request_type=dict) +def test_update_intent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: + client.update_intent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_intent.UpdateIntentRequest() + + @pytest.mark.asyncio async def test_update_intent_async( transport: str = "grpc_asyncio", request_type=gcd_intent.UpdateIntentRequest @@ -1402,6 +1499,8 @@ async def test_update_intent_async( priority=898, is_fallback=True, ml_disabled=True, + live_agent_handoff=True, + end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], action="action_value", @@ -1439,6 +1538,10 @@ async def test_update_intent_async( assert response.ml_disabled is True + assert response.live_agent_handoff is True + + assert response.end_interaction is True + assert response.input_context_names == ["input_context_names_value"] assert response.events == ["events_value"] @@ -1629,6 +1732,22 @@ def test_delete_intent_from_dict(): test_delete_intent(request_type=dict) +def test_delete_intent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_intent), "__call__") as call: + client.delete_intent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.DeleteIntentRequest() + + @pytest.mark.asyncio async def test_delete_intent_async( transport: str = "grpc_asyncio", request_type=intent.DeleteIntentRequest @@ -1811,6 +1930,24 @@ def test_batch_update_intents_from_dict(): test_batch_update_intents(request_type=dict) +def test_batch_update_intents_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_update_intents), "__call__" + ) as call: + client.batch_update_intents() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.BatchUpdateIntentsRequest() + + @pytest.mark.asyncio async def test_batch_update_intents_async( transport: str = "grpc_asyncio", request_type=intent.BatchUpdateIntentsRequest @@ -2039,6 +2176,24 @@ def test_batch_delete_intents_from_dict(): test_batch_delete_intents(request_type=dict) +def test_batch_delete_intents_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_delete_intents), "__call__" + ) as call: + client.batch_delete_intents() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.BatchDeleteIntentsRequest() + + @pytest.mark.asyncio async def test_batch_delete_intents_async( transport: str = "grpc_asyncio", request_type=intent.BatchDeleteIntentsRequest @@ -2271,7 +2426,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], + [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -2391,6 +2546,51 @@ def test_intents_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], +) +def test_intents_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_intents_host_no_port(): client = IntentsClient( credentials=credentials.AnonymousCredentials(), @@ -2412,7 +2612,7 @@ def test_intents_host_with_port(): def test_intents_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.IntentsGrpcTransport( @@ -2424,7 +2624,7 @@ def test_intents_grpc_transport_channel(): def test_intents_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.IntentsGrpcAsyncIOTransport( @@ -2435,6 +2635,8 @@ def test_intents_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], @@ -2444,7 +2646,7 @@ def test_intents_transport_channel_mtls_with_client_cert_source(transport_class) "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -2485,6 +2687,8 @@ def test_intents_transport_channel_mtls_with_client_cert_source(transport_class) assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], @@ -2497,7 +2701,7 @@ def test_intents_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2/test_knowledge_bases.py b/tests/unit/gapic/dialogflow_v2/test_knowledge_bases.py new file mode 100644 index 000000000..d20501047 --- /dev/null +++ b/tests/unit/gapic/dialogflow_v2/test_knowledge_bases.py @@ -0,0 +1,2323 @@ +# -*- 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.dialogflow_v2.services.knowledge_bases import ( + KnowledgeBasesAsyncClient, +) +from google.cloud.dialogflow_v2.services.knowledge_bases import KnowledgeBasesClient +from google.cloud.dialogflow_v2.services.knowledge_bases import pagers +from google.cloud.dialogflow_v2.services.knowledge_bases import transports +from google.cloud.dialogflow_v2.types import knowledge_base +from google.cloud.dialogflow_v2.types import knowledge_base as gcd_knowledge_base +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 KnowledgeBasesClient._get_default_mtls_endpoint(None) is None + assert ( + KnowledgeBasesClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + KnowledgeBasesClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + KnowledgeBasesClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + KnowledgeBasesClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + KnowledgeBasesClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [KnowledgeBasesClient, KnowledgeBasesAsyncClient,] +) +def test_knowledge_bases_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize( + "client_class", [KnowledgeBasesClient, KnowledgeBasesAsyncClient,] +) +def test_knowledge_bases_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 + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_knowledge_bases_client_get_transport_class(): + transport = KnowledgeBasesClient.get_transport_class() + available_transports = [ + transports.KnowledgeBasesGrpcTransport, + ] + assert transport in available_transports + + transport = KnowledgeBasesClient.get_transport_class("grpc") + assert transport == transports.KnowledgeBasesGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (KnowledgeBasesClient, transports.KnowledgeBasesGrpcTransport, "grpc"), + ( + KnowledgeBasesAsyncClient, + transports.KnowledgeBasesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + KnowledgeBasesClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(KnowledgeBasesClient), +) +@mock.patch.object( + KnowledgeBasesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(KnowledgeBasesAsyncClient), +) +def test_knowledge_bases_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(KnowledgeBasesClient, "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(KnowledgeBasesClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (KnowledgeBasesClient, transports.KnowledgeBasesGrpcTransport, "grpc", "true"), + ( + KnowledgeBasesAsyncClient, + transports.KnowledgeBasesGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (KnowledgeBasesClient, transports.KnowledgeBasesGrpcTransport, "grpc", "false"), + ( + KnowledgeBasesAsyncClient, + transports.KnowledgeBasesGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + KnowledgeBasesClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(KnowledgeBasesClient), +) +@mock.patch.object( + KnowledgeBasesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(KnowledgeBasesAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_knowledge_bases_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (KnowledgeBasesClient, transports.KnowledgeBasesGrpcTransport, "grpc"), + ( + KnowledgeBasesAsyncClient, + transports.KnowledgeBasesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_knowledge_bases_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (KnowledgeBasesClient, transports.KnowledgeBasesGrpcTransport, "grpc"), + ( + KnowledgeBasesAsyncClient, + transports.KnowledgeBasesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_knowledge_bases_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_knowledge_bases_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflow_v2.services.knowledge_bases.transports.KnowledgeBasesGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = KnowledgeBasesClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_knowledge_bases( + transport: str = "grpc", request_type=knowledge_base.ListKnowledgeBasesRequest +): + client = KnowledgeBasesClient( + 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_knowledge_bases), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = knowledge_base.ListKnowledgeBasesResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_knowledge_bases(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.ListKnowledgeBasesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListKnowledgeBasesPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_knowledge_bases_from_dict(): + test_list_knowledge_bases(request_type=dict) + + +def test_list_knowledge_bases_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_knowledge_bases), "__call__" + ) as call: + client.list_knowledge_bases() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.ListKnowledgeBasesRequest() + + +@pytest.mark.asyncio +async def test_list_knowledge_bases_async( + transport: str = "grpc_asyncio", + request_type=knowledge_base.ListKnowledgeBasesRequest, +): + client = KnowledgeBasesAsyncClient( + 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_knowledge_bases), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + knowledge_base.ListKnowledgeBasesResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_knowledge_bases(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.ListKnowledgeBasesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListKnowledgeBasesAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_knowledge_bases_async_from_dict(): + await test_list_knowledge_bases_async(request_type=dict) + + +def test_list_knowledge_bases_field_headers(): + client = KnowledgeBasesClient(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 = knowledge_base.ListKnowledgeBasesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_knowledge_bases), "__call__" + ) as call: + call.return_value = knowledge_base.ListKnowledgeBasesResponse() + + client.list_knowledge_bases(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_knowledge_bases_field_headers_async(): + client = KnowledgeBasesAsyncClient(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 = knowledge_base.ListKnowledgeBasesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_knowledge_bases), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + knowledge_base.ListKnowledgeBasesResponse() + ) + + await client.list_knowledge_bases(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_knowledge_bases_flattened(): + client = KnowledgeBasesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_knowledge_bases), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = knowledge_base.ListKnowledgeBasesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_knowledge_bases(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_knowledge_bases_flattened_error(): + client = KnowledgeBasesClient(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_knowledge_bases( + knowledge_base.ListKnowledgeBasesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_knowledge_bases_flattened_async(): + client = KnowledgeBasesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_knowledge_bases), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = knowledge_base.ListKnowledgeBasesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + knowledge_base.ListKnowledgeBasesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_knowledge_bases(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_knowledge_bases_flattened_error_async(): + client = KnowledgeBasesAsyncClient(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_knowledge_bases( + knowledge_base.ListKnowledgeBasesRequest(), parent="parent_value", + ) + + +def test_list_knowledge_bases_pager(): + client = KnowledgeBasesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_knowledge_bases), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[ + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + ], + next_page_token="abc", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[], next_page_token="def", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[knowledge_base.KnowledgeBase(),], + next_page_token="ghi", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[ + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + ], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_knowledge_bases(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, knowledge_base.KnowledgeBase) for i in results) + + +def test_list_knowledge_bases_pages(): + client = KnowledgeBasesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_knowledge_bases), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[ + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + ], + next_page_token="abc", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[], next_page_token="def", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[knowledge_base.KnowledgeBase(),], + next_page_token="ghi", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[ + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + ], + ), + RuntimeError, + ) + pages = list(client.list_knowledge_bases(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_knowledge_bases_async_pager(): + client = KnowledgeBasesAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_knowledge_bases), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[ + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + ], + next_page_token="abc", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[], next_page_token="def", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[knowledge_base.KnowledgeBase(),], + next_page_token="ghi", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[ + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + ], + ), + RuntimeError, + ) + async_pager = await client.list_knowledge_bases(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, knowledge_base.KnowledgeBase) for i in responses) + + +@pytest.mark.asyncio +async def test_list_knowledge_bases_async_pages(): + client = KnowledgeBasesAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_knowledge_bases), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[ + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + ], + next_page_token="abc", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[], next_page_token="def", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[knowledge_base.KnowledgeBase(),], + next_page_token="ghi", + ), + knowledge_base.ListKnowledgeBasesResponse( + knowledge_bases=[ + knowledge_base.KnowledgeBase(), + knowledge_base.KnowledgeBase(), + ], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_knowledge_bases(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_knowledge_base( + transport: str = "grpc", request_type=knowledge_base.GetKnowledgeBaseRequest +): + client = KnowledgeBasesClient( + 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_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = knowledge_base.KnowledgeBase( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + + response = client.get_knowledge_base(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.GetKnowledgeBaseRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, knowledge_base.KnowledgeBase) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +def test_get_knowledge_base_from_dict(): + test_get_knowledge_base(request_type=dict) + + +def test_get_knowledge_base_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_knowledge_base), "__call__" + ) as call: + client.get_knowledge_base() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.GetKnowledgeBaseRequest() + + +@pytest.mark.asyncio +async def test_get_knowledge_base_async( + transport: str = "grpc_asyncio", request_type=knowledge_base.GetKnowledgeBaseRequest +): + client = KnowledgeBasesAsyncClient( + 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_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + knowledge_base.KnowledgeBase( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + ) + + response = await client.get_knowledge_base(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.GetKnowledgeBaseRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, knowledge_base.KnowledgeBase) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +@pytest.mark.asyncio +async def test_get_knowledge_base_async_from_dict(): + await test_get_knowledge_base_async(request_type=dict) + + +def test_get_knowledge_base_field_headers(): + client = KnowledgeBasesClient(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 = knowledge_base.GetKnowledgeBaseRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_knowledge_base), "__call__" + ) as call: + call.return_value = knowledge_base.KnowledgeBase() + + client.get_knowledge_base(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_knowledge_base_field_headers_async(): + client = KnowledgeBasesAsyncClient(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 = knowledge_base.GetKnowledgeBaseRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_knowledge_base), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + knowledge_base.KnowledgeBase() + ) + + await client.get_knowledge_base(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_knowledge_base_flattened(): + client = KnowledgeBasesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = knowledge_base.KnowledgeBase() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_knowledge_base(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_knowledge_base_flattened_error(): + client = KnowledgeBasesClient(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_knowledge_base( + knowledge_base.GetKnowledgeBaseRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_knowledge_base_flattened_async(): + client = KnowledgeBasesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = knowledge_base.KnowledgeBase() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + knowledge_base.KnowledgeBase() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_knowledge_base(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_knowledge_base_flattened_error_async(): + client = KnowledgeBasesAsyncClient(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_knowledge_base( + knowledge_base.GetKnowledgeBaseRequest(), name="name_value", + ) + + +def test_create_knowledge_base( + transport: str = "grpc", request_type=gcd_knowledge_base.CreateKnowledgeBaseRequest +): + client = KnowledgeBasesClient( + 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_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_knowledge_base.KnowledgeBase( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + + response = client.create_knowledge_base(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_knowledge_base.CreateKnowledgeBaseRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_knowledge_base.KnowledgeBase) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +def test_create_knowledge_base_from_dict(): + test_create_knowledge_base(request_type=dict) + + +def test_create_knowledge_base_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_knowledge_base), "__call__" + ) as call: + client.create_knowledge_base() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_knowledge_base.CreateKnowledgeBaseRequest() + + +@pytest.mark.asyncio +async def test_create_knowledge_base_async( + transport: str = "grpc_asyncio", + request_type=gcd_knowledge_base.CreateKnowledgeBaseRequest, +): + client = KnowledgeBasesAsyncClient( + 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_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_knowledge_base.KnowledgeBase( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + ) + + response = await client.create_knowledge_base(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_knowledge_base.CreateKnowledgeBaseRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_knowledge_base.KnowledgeBase) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +@pytest.mark.asyncio +async def test_create_knowledge_base_async_from_dict(): + await test_create_knowledge_base_async(request_type=dict) + + +def test_create_knowledge_base_field_headers(): + client = KnowledgeBasesClient(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 = gcd_knowledge_base.CreateKnowledgeBaseRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_knowledge_base), "__call__" + ) as call: + call.return_value = gcd_knowledge_base.KnowledgeBase() + + client.create_knowledge_base(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_knowledge_base_field_headers_async(): + client = KnowledgeBasesAsyncClient(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 = gcd_knowledge_base.CreateKnowledgeBaseRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_knowledge_base), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_knowledge_base.KnowledgeBase() + ) + + await client.create_knowledge_base(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_knowledge_base_flattened(): + client = KnowledgeBasesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_knowledge_base.KnowledgeBase() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_knowledge_base( + parent="parent_value", + knowledge_base=gcd_knowledge_base.KnowledgeBase(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].knowledge_base == gcd_knowledge_base.KnowledgeBase( + name="name_value" + ) + + +def test_create_knowledge_base_flattened_error(): + client = KnowledgeBasesClient(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_knowledge_base( + gcd_knowledge_base.CreateKnowledgeBaseRequest(), + parent="parent_value", + knowledge_base=gcd_knowledge_base.KnowledgeBase(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_knowledge_base_flattened_async(): + client = KnowledgeBasesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_knowledge_base.KnowledgeBase() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_knowledge_base.KnowledgeBase() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_knowledge_base( + parent="parent_value", + knowledge_base=gcd_knowledge_base.KnowledgeBase(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].knowledge_base == gcd_knowledge_base.KnowledgeBase( + name="name_value" + ) + + +@pytest.mark.asyncio +async def test_create_knowledge_base_flattened_error_async(): + client = KnowledgeBasesAsyncClient(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_knowledge_base( + gcd_knowledge_base.CreateKnowledgeBaseRequest(), + parent="parent_value", + knowledge_base=gcd_knowledge_base.KnowledgeBase(name="name_value"), + ) + + +def test_delete_knowledge_base( + transport: str = "grpc", request_type=knowledge_base.DeleteKnowledgeBaseRequest +): + client = KnowledgeBasesClient( + 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_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_knowledge_base(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.DeleteKnowledgeBaseRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_knowledge_base_from_dict(): + test_delete_knowledge_base(request_type=dict) + + +def test_delete_knowledge_base_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_knowledge_base), "__call__" + ) as call: + client.delete_knowledge_base() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.DeleteKnowledgeBaseRequest() + + +@pytest.mark.asyncio +async def test_delete_knowledge_base_async( + transport: str = "grpc_asyncio", + request_type=knowledge_base.DeleteKnowledgeBaseRequest, +): + client = KnowledgeBasesAsyncClient( + 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_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_knowledge_base(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.DeleteKnowledgeBaseRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_knowledge_base_async_from_dict(): + await test_delete_knowledge_base_async(request_type=dict) + + +def test_delete_knowledge_base_field_headers(): + client = KnowledgeBasesClient(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 = knowledge_base.DeleteKnowledgeBaseRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_knowledge_base), "__call__" + ) as call: + call.return_value = None + + client.delete_knowledge_base(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_knowledge_base_field_headers_async(): + client = KnowledgeBasesAsyncClient(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 = knowledge_base.DeleteKnowledgeBaseRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_knowledge_base), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_knowledge_base(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_knowledge_base_flattened(): + client = KnowledgeBasesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_knowledge_base), "__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_knowledge_base(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_knowledge_base_flattened_error(): + client = KnowledgeBasesClient(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_knowledge_base( + knowledge_base.DeleteKnowledgeBaseRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_knowledge_base_flattened_async(): + client = KnowledgeBasesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_knowledge_base), "__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_knowledge_base(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_knowledge_base_flattened_error_async(): + client = KnowledgeBasesAsyncClient(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_knowledge_base( + knowledge_base.DeleteKnowledgeBaseRequest(), name="name_value", + ) + + +def test_update_knowledge_base( + transport: str = "grpc", request_type=gcd_knowledge_base.UpdateKnowledgeBaseRequest +): + client = KnowledgeBasesClient( + 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_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_knowledge_base.KnowledgeBase( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + + response = client.update_knowledge_base(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_knowledge_base.UpdateKnowledgeBaseRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_knowledge_base.KnowledgeBase) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +def test_update_knowledge_base_from_dict(): + test_update_knowledge_base(request_type=dict) + + +def test_update_knowledge_base_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_knowledge_base), "__call__" + ) as call: + client.update_knowledge_base() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_knowledge_base.UpdateKnowledgeBaseRequest() + + +@pytest.mark.asyncio +async def test_update_knowledge_base_async( + transport: str = "grpc_asyncio", + request_type=gcd_knowledge_base.UpdateKnowledgeBaseRequest, +): + client = KnowledgeBasesAsyncClient( + 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_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_knowledge_base.KnowledgeBase( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + ) + + response = await client.update_knowledge_base(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_knowledge_base.UpdateKnowledgeBaseRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_knowledge_base.KnowledgeBase) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +@pytest.mark.asyncio +async def test_update_knowledge_base_async_from_dict(): + await test_update_knowledge_base_async(request_type=dict) + + +def test_update_knowledge_base_field_headers(): + client = KnowledgeBasesClient(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 = gcd_knowledge_base.UpdateKnowledgeBaseRequest() + request.knowledge_base.name = "knowledge_base.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_knowledge_base), "__call__" + ) as call: + call.return_value = gcd_knowledge_base.KnowledgeBase() + + client.update_knowledge_base(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", + "knowledge_base.name=knowledge_base.name/value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_knowledge_base_field_headers_async(): + client = KnowledgeBasesAsyncClient(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 = gcd_knowledge_base.UpdateKnowledgeBaseRequest() + request.knowledge_base.name = "knowledge_base.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_knowledge_base), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_knowledge_base.KnowledgeBase() + ) + + await client.update_knowledge_base(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", + "knowledge_base.name=knowledge_base.name/value", + ) in kw["metadata"] + + +def test_update_knowledge_base_flattened(): + client = KnowledgeBasesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_knowledge_base.KnowledgeBase() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_knowledge_base( + knowledge_base=gcd_knowledge_base.KnowledgeBase(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].knowledge_base == gcd_knowledge_base.KnowledgeBase( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_knowledge_base_flattened_error(): + client = KnowledgeBasesClient(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_knowledge_base( + gcd_knowledge_base.UpdateKnowledgeBaseRequest(), + knowledge_base=gcd_knowledge_base.KnowledgeBase(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_knowledge_base_flattened_async(): + client = KnowledgeBasesAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_knowledge_base), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_knowledge_base.KnowledgeBase() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_knowledge_base.KnowledgeBase() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_knowledge_base( + knowledge_base=gcd_knowledge_base.KnowledgeBase(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].knowledge_base == gcd_knowledge_base.KnowledgeBase( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_knowledge_base_flattened_error_async(): + client = KnowledgeBasesAsyncClient(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_knowledge_base( + gcd_knowledge_base.UpdateKnowledgeBaseRequest(), + knowledge_base=gcd_knowledge_base.KnowledgeBase(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.KnowledgeBasesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.KnowledgeBasesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = KnowledgeBasesClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.KnowledgeBasesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = KnowledgeBasesClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.KnowledgeBasesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = KnowledgeBasesClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.KnowledgeBasesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.KnowledgeBasesGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.KnowledgeBasesGrpcTransport, + transports.KnowledgeBasesGrpcAsyncIOTransport, + ], +) +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 = KnowledgeBasesClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.KnowledgeBasesGrpcTransport,) + + +def test_knowledge_bases_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.KnowledgeBasesTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_knowledge_bases_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflow_v2.services.knowledge_bases.transports.KnowledgeBasesTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.KnowledgeBasesTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_knowledge_bases", + "get_knowledge_base", + "create_knowledge_base", + "delete_knowledge_base", + "update_knowledge_base", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_knowledge_bases_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.dialogflow_v2.services.knowledge_bases.transports.KnowledgeBasesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.KnowledgeBasesTransport( + 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_knowledge_bases_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.dialogflow_v2.services.knowledge_bases.transports.KnowledgeBasesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.KnowledgeBasesTransport() + adc.assert_called_once() + + +def test_knowledge_bases_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) + KnowledgeBasesClient() + 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_knowledge_bases_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.KnowledgeBasesGrpcTransport( + 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", + ) + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.KnowledgeBasesGrpcTransport, + transports.KnowledgeBasesGrpcAsyncIOTransport, + ], +) +def test_knowledge_bases_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_knowledge_bases_host_no_port(): + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_knowledge_bases_host_with_port(): + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_knowledge_bases_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.KnowledgeBasesGrpcTransport( + 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_knowledge_bases_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.KnowledgeBasesGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.KnowledgeBasesGrpcTransport, + transports.KnowledgeBasesGrpcAsyncIOTransport, + ], +) +def test_knowledge_bases_transport_channel_mtls_with_client_cert_source( + transport_class, +): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.KnowledgeBasesGrpcTransport, + transports.KnowledgeBasesGrpcAsyncIOTransport, + ], +) +def test_knowledge_bases_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_knowledge_base_path(): + project = "squid" + knowledge_base = "clam" + + expected = "projects/{project}/knowledgeBases/{knowledge_base}".format( + project=project, knowledge_base=knowledge_base, + ) + actual = KnowledgeBasesClient.knowledge_base_path(project, knowledge_base) + assert expected == actual + + +def test_parse_knowledge_base_path(): + expected = { + "project": "whelk", + "knowledge_base": "octopus", + } + path = KnowledgeBasesClient.knowledge_base_path(**expected) + + # Check that the path construction is reversible. + actual = KnowledgeBasesClient.parse_knowledge_base_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "oyster" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = KnowledgeBasesClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nudibranch", + } + path = KnowledgeBasesClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = KnowledgeBasesClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "cuttlefish" + + expected = "folders/{folder}".format(folder=folder,) + actual = KnowledgeBasesClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "mussel", + } + path = KnowledgeBasesClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = KnowledgeBasesClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "winkle" + + expected = "organizations/{organization}".format(organization=organization,) + actual = KnowledgeBasesClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nautilus", + } + path = KnowledgeBasesClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = KnowledgeBasesClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "scallop" + + expected = "projects/{project}".format(project=project,) + actual = KnowledgeBasesClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "abalone", + } + path = KnowledgeBasesClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = KnowledgeBasesClient.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 = KnowledgeBasesClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "whelk", + "location": "octopus", + } + path = KnowledgeBasesClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = KnowledgeBasesClient.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.KnowledgeBasesTransport, "_prep_wrapped_messages" + ) as prep: + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.KnowledgeBasesTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = KnowledgeBasesClient.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/dialogflow_v2/test_participants.py b/tests/unit/gapic/dialogflow_v2/test_participants.py new file mode 100644 index 000000000..705bec0ff --- /dev/null +++ b/tests/unit/gapic/dialogflow_v2/test_participants.py @@ -0,0 +1,2926 @@ +# -*- 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.dialogflow_v2.services.participants import ParticipantsAsyncClient +from google.cloud.dialogflow_v2.services.participants import ParticipantsClient +from google.cloud.dialogflow_v2.services.participants import pagers +from google.cloud.dialogflow_v2.services.participants import transports +from google.cloud.dialogflow_v2.types import audio_config +from google.cloud.dialogflow_v2.types import context +from google.cloud.dialogflow_v2.types import entity_type +from google.cloud.dialogflow_v2.types import participant +from google.cloud.dialogflow_v2.types import participant as gcd_participant +from google.cloud.dialogflow_v2.types import session +from google.cloud.dialogflow_v2.types import session_entity_type +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.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 ParticipantsClient._get_default_mtls_endpoint(None) is None + assert ( + ParticipantsClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + ) + assert ( + ParticipantsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + ParticipantsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ParticipantsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ParticipantsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [ParticipantsClient, ParticipantsAsyncClient,]) +def test_participants_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [ParticipantsClient, ParticipantsAsyncClient,]) +def test_participants_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 + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_participants_client_get_transport_class(): + transport = ParticipantsClient.get_transport_class() + available_transports = [ + transports.ParticipantsGrpcTransport, + ] + assert transport in available_transports + + transport = ParticipantsClient.get_transport_class("grpc") + assert transport == transports.ParticipantsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ParticipantsClient, transports.ParticipantsGrpcTransport, "grpc"), + ( + ParticipantsAsyncClient, + transports.ParticipantsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + ParticipantsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(ParticipantsClient) +) +@mock.patch.object( + ParticipantsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ParticipantsAsyncClient), +) +def test_participants_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(ParticipantsClient, "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(ParticipantsClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (ParticipantsClient, transports.ParticipantsGrpcTransport, "grpc", "true"), + ( + ParticipantsAsyncClient, + transports.ParticipantsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (ParticipantsClient, transports.ParticipantsGrpcTransport, "grpc", "false"), + ( + ParticipantsAsyncClient, + transports.ParticipantsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + ParticipantsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(ParticipantsClient) +) +@mock.patch.object( + ParticipantsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ParticipantsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_participants_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ParticipantsClient, transports.ParticipantsGrpcTransport, "grpc"), + ( + ParticipantsAsyncClient, + transports.ParticipantsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_participants_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ParticipantsClient, transports.ParticipantsGrpcTransport, "grpc"), + ( + ParticipantsAsyncClient, + transports.ParticipantsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_participants_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_participants_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflow_v2.services.participants.transports.ParticipantsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = ParticipantsClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_create_participant( + transport: str = "grpc", request_type=gcd_participant.CreateParticipantRequest +): + client = ParticipantsClient( + 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_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant( + name="name_value", + role=gcd_participant.Participant.Role.HUMAN_AGENT, + sip_recording_media_label="sip_recording_media_label_value", + ) + + response = client.create_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.CreateParticipantRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_participant.Participant) + + assert response.name == "name_value" + + assert response.role == gcd_participant.Participant.Role.HUMAN_AGENT + + assert response.sip_recording_media_label == "sip_recording_media_label_value" + + +def test_create_participant_from_dict(): + test_create_participant(request_type=dict) + + +def test_create_participant_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_participant), "__call__" + ) as call: + client.create_participant() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.CreateParticipantRequest() + + +@pytest.mark.asyncio +async def test_create_participant_async( + transport: str = "grpc_asyncio", + request_type=gcd_participant.CreateParticipantRequest, +): + client = ParticipantsAsyncClient( + 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_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant( + name="name_value", + role=gcd_participant.Participant.Role.HUMAN_AGENT, + sip_recording_media_label="sip_recording_media_label_value", + ) + ) + + response = await client.create_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.CreateParticipantRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_participant.Participant) + + assert response.name == "name_value" + + assert response.role == gcd_participant.Participant.Role.HUMAN_AGENT + + assert response.sip_recording_media_label == "sip_recording_media_label_value" + + +@pytest.mark.asyncio +async def test_create_participant_async_from_dict(): + await test_create_participant_async(request_type=dict) + + +def test_create_participant_field_headers(): + client = ParticipantsClient(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 = gcd_participant.CreateParticipantRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_participant), "__call__" + ) as call: + call.return_value = gcd_participant.Participant() + + client.create_participant(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_participant_field_headers_async(): + client = ParticipantsAsyncClient(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 = gcd_participant.CreateParticipantRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_participant), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant() + ) + + await client.create_participant(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_participant_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_participant( + parent="parent_value", + participant=gcd_participant.Participant(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].participant == gcd_participant.Participant(name="name_value") + + +def test_create_participant_flattened_error(): + client = ParticipantsClient(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_participant( + gcd_participant.CreateParticipantRequest(), + parent="parent_value", + participant=gcd_participant.Participant(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_participant_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_participant( + parent="parent_value", + participant=gcd_participant.Participant(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].participant == gcd_participant.Participant(name="name_value") + + +@pytest.mark.asyncio +async def test_create_participant_flattened_error_async(): + client = ParticipantsAsyncClient(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_participant( + gcd_participant.CreateParticipantRequest(), + parent="parent_value", + participant=gcd_participant.Participant(name="name_value"), + ) + + +def test_get_participant( + transport: str = "grpc", request_type=participant.GetParticipantRequest +): + client = ParticipantsClient( + 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_participant), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.Participant( + name="name_value", + role=participant.Participant.Role.HUMAN_AGENT, + sip_recording_media_label="sip_recording_media_label_value", + ) + + response = client.get_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.GetParticipantRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, participant.Participant) + + assert response.name == "name_value" + + assert response.role == participant.Participant.Role.HUMAN_AGENT + + assert response.sip_recording_media_label == "sip_recording_media_label_value" + + +def test_get_participant_from_dict(): + test_get_participant(request_type=dict) + + +def test_get_participant_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_participant), "__call__") as call: + client.get_participant() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.GetParticipantRequest() + + +@pytest.mark.asyncio +async def test_get_participant_async( + transport: str = "grpc_asyncio", request_type=participant.GetParticipantRequest +): + client = ParticipantsAsyncClient( + 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_participant), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.Participant( + name="name_value", + role=participant.Participant.Role.HUMAN_AGENT, + sip_recording_media_label="sip_recording_media_label_value", + ) + ) + + response = await client.get_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.GetParticipantRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, participant.Participant) + + assert response.name == "name_value" + + assert response.role == participant.Participant.Role.HUMAN_AGENT + + assert response.sip_recording_media_label == "sip_recording_media_label_value" + + +@pytest.mark.asyncio +async def test_get_participant_async_from_dict(): + await test_get_participant_async(request_type=dict) + + +def test_get_participant_field_headers(): + client = ParticipantsClient(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 = participant.GetParticipantRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_participant), "__call__") as call: + call.return_value = participant.Participant() + + client.get_participant(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_participant_field_headers_async(): + client = ParticipantsAsyncClient(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 = participant.GetParticipantRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_participant), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.Participant() + ) + + await client.get_participant(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_participant_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_participant), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.Participant() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_participant(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_participant_flattened_error(): + client = ParticipantsClient(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_participant( + participant.GetParticipantRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_participant_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_participant), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.Participant() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.Participant() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_participant(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_participant_flattened_error_async(): + client = ParticipantsAsyncClient(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_participant( + participant.GetParticipantRequest(), name="name_value", + ) + + +def test_list_participants( + transport: str = "grpc", request_type=participant.ListParticipantsRequest +): + client = ParticipantsClient( + 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_participants), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.ListParticipantsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_participants(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.ListParticipantsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListParticipantsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_participants_from_dict(): + test_list_participants(request_type=dict) + + +def test_list_participants_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + client.list_participants() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.ListParticipantsRequest() + + +@pytest.mark.asyncio +async def test_list_participants_async( + transport: str = "grpc_asyncio", request_type=participant.ListParticipantsRequest +): + client = ParticipantsAsyncClient( + 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_participants), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.ListParticipantsResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_participants(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.ListParticipantsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListParticipantsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_participants_async_from_dict(): + await test_list_participants_async(request_type=dict) + + +def test_list_participants_field_headers(): + client = ParticipantsClient(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 = participant.ListParticipantsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + call.return_value = participant.ListParticipantsResponse() + + client.list_participants(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_participants_field_headers_async(): + client = ParticipantsAsyncClient(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 = participant.ListParticipantsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.ListParticipantsResponse() + ) + + await client.list_participants(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_participants_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.ListParticipantsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_participants(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_participants_flattened_error(): + client = ParticipantsClient(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_participants( + participant.ListParticipantsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_participants_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.ListParticipantsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.ListParticipantsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_participants(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_participants_flattened_error_async(): + client = ParticipantsAsyncClient(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_participants( + participant.ListParticipantsRequest(), parent="parent_value", + ) + + +def test_list_participants_pager(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListParticipantsResponse( + participants=[ + participant.Participant(), + participant.Participant(), + participant.Participant(), + ], + next_page_token="abc", + ), + participant.ListParticipantsResponse( + participants=[], next_page_token="def", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(),], next_page_token="ghi", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(), participant.Participant(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_participants(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, participant.Participant) for i in results) + + +def test_list_participants_pages(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListParticipantsResponse( + participants=[ + participant.Participant(), + participant.Participant(), + participant.Participant(), + ], + next_page_token="abc", + ), + participant.ListParticipantsResponse( + participants=[], next_page_token="def", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(),], next_page_token="ghi", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(), participant.Participant(),], + ), + RuntimeError, + ) + pages = list(client.list_participants(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_participants_async_pager(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListParticipantsResponse( + participants=[ + participant.Participant(), + participant.Participant(), + participant.Participant(), + ], + next_page_token="abc", + ), + participant.ListParticipantsResponse( + participants=[], next_page_token="def", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(),], next_page_token="ghi", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(), participant.Participant(),], + ), + RuntimeError, + ) + async_pager = await client.list_participants(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, participant.Participant) for i in responses) + + +@pytest.mark.asyncio +async def test_list_participants_async_pages(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListParticipantsResponse( + participants=[ + participant.Participant(), + participant.Participant(), + participant.Participant(), + ], + next_page_token="abc", + ), + participant.ListParticipantsResponse( + participants=[], next_page_token="def", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(),], next_page_token="ghi", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(), participant.Participant(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_participants(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_update_participant( + transport: str = "grpc", request_type=gcd_participant.UpdateParticipantRequest +): + client = ParticipantsClient( + 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_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant( + name="name_value", + role=gcd_participant.Participant.Role.HUMAN_AGENT, + sip_recording_media_label="sip_recording_media_label_value", + ) + + response = client.update_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.UpdateParticipantRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_participant.Participant) + + assert response.name == "name_value" + + assert response.role == gcd_participant.Participant.Role.HUMAN_AGENT + + assert response.sip_recording_media_label == "sip_recording_media_label_value" + + +def test_update_participant_from_dict(): + test_update_participant(request_type=dict) + + +def test_update_participant_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_participant), "__call__" + ) as call: + client.update_participant() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.UpdateParticipantRequest() + + +@pytest.mark.asyncio +async def test_update_participant_async( + transport: str = "grpc_asyncio", + request_type=gcd_participant.UpdateParticipantRequest, +): + client = ParticipantsAsyncClient( + 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_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant( + name="name_value", + role=gcd_participant.Participant.Role.HUMAN_AGENT, + sip_recording_media_label="sip_recording_media_label_value", + ) + ) + + response = await client.update_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.UpdateParticipantRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_participant.Participant) + + assert response.name == "name_value" + + assert response.role == gcd_participant.Participant.Role.HUMAN_AGENT + + assert response.sip_recording_media_label == "sip_recording_media_label_value" + + +@pytest.mark.asyncio +async def test_update_participant_async_from_dict(): + await test_update_participant_async(request_type=dict) + + +def test_update_participant_field_headers(): + client = ParticipantsClient(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 = gcd_participant.UpdateParticipantRequest() + request.participant.name = "participant.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_participant), "__call__" + ) as call: + call.return_value = gcd_participant.Participant() + + client.update_participant(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", "participant.name=participant.name/value",) in kw[ + "metadata" + ] + + +@pytest.mark.asyncio +async def test_update_participant_field_headers_async(): + client = ParticipantsAsyncClient(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 = gcd_participant.UpdateParticipantRequest() + request.participant.name = "participant.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_participant), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant() + ) + + await client.update_participant(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", "participant.name=participant.name/value",) in kw[ + "metadata" + ] + + +def test_update_participant_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_participant( + participant=gcd_participant.Participant(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].participant == gcd_participant.Participant(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_participant_flattened_error(): + client = ParticipantsClient(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_participant( + gcd_participant.UpdateParticipantRequest(), + participant=gcd_participant.Participant(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_participant_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_participant( + participant=gcd_participant.Participant(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].participant == gcd_participant.Participant(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_participant_flattened_error_async(): + client = ParticipantsAsyncClient(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_participant( + gcd_participant.UpdateParticipantRequest(), + participant=gcd_participant.Participant(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_analyze_content( + transport: str = "grpc", request_type=gcd_participant.AnalyzeContentRequest +): + client = ParticipantsClient( + 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.analyze_content), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.AnalyzeContentResponse( + reply_text="reply_text_value", + ) + + response = client.analyze_content(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.AnalyzeContentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_participant.AnalyzeContentResponse) + + assert response.reply_text == "reply_text_value" + + +def test_analyze_content_from_dict(): + test_analyze_content(request_type=dict) + + +def test_analyze_content_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.analyze_content), "__call__") as call: + client.analyze_content() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.AnalyzeContentRequest() + + +@pytest.mark.asyncio +async def test_analyze_content_async( + transport: str = "grpc_asyncio", request_type=gcd_participant.AnalyzeContentRequest +): + client = ParticipantsAsyncClient( + 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.analyze_content), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.AnalyzeContentResponse(reply_text="reply_text_value",) + ) + + response = await client.analyze_content(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.AnalyzeContentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_participant.AnalyzeContentResponse) + + assert response.reply_text == "reply_text_value" + + +@pytest.mark.asyncio +async def test_analyze_content_async_from_dict(): + await test_analyze_content_async(request_type=dict) + + +def test_analyze_content_field_headers(): + client = ParticipantsClient(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 = gcd_participant.AnalyzeContentRequest() + request.participant = "participant/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.analyze_content), "__call__") as call: + call.return_value = gcd_participant.AnalyzeContentResponse() + + client.analyze_content(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", "participant=participant/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_analyze_content_field_headers_async(): + client = ParticipantsAsyncClient(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 = gcd_participant.AnalyzeContentRequest() + request.participant = "participant/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.analyze_content), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.AnalyzeContentResponse() + ) + + await client.analyze_content(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", "participant=participant/value",) in kw["metadata"] + + +def test_analyze_content_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.analyze_content), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.AnalyzeContentResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.analyze_content( + participant="participant_value", + text_input=session.TextInput(text="text_value"), + audio_input=gcd_participant.AudioInput( + config=audio_config.InputAudioConfig( + audio_encoding=audio_config.AudioEncoding.AUDIO_ENCODING_LINEAR_16 + ) + ), + event_input=session.EventInput(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].participant == "participant_value" + + assert args[0].event_input == session.EventInput(name="name_value") + + +def test_analyze_content_flattened_error(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.analyze_content( + gcd_participant.AnalyzeContentRequest(), + participant="participant_value", + text_input=session.TextInput(text="text_value"), + audio_input=gcd_participant.AudioInput( + config=audio_config.InputAudioConfig( + audio_encoding=audio_config.AudioEncoding.AUDIO_ENCODING_LINEAR_16 + ) + ), + event_input=session.EventInput(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_analyze_content_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.analyze_content), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.AnalyzeContentResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.AnalyzeContentResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.analyze_content( + participant="participant_value", + text_input=session.TextInput(text="text_value"), + audio_input=gcd_participant.AudioInput( + config=audio_config.InputAudioConfig( + audio_encoding=audio_config.AudioEncoding.AUDIO_ENCODING_LINEAR_16 + ) + ), + event_input=session.EventInput(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].participant == "participant_value" + + assert args[0].event_input == session.EventInput(name="name_value") + + +@pytest.mark.asyncio +async def test_analyze_content_flattened_error_async(): + client = ParticipantsAsyncClient(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.analyze_content( + gcd_participant.AnalyzeContentRequest(), + participant="participant_value", + text_input=session.TextInput(text="text_value"), + audio_input=gcd_participant.AudioInput( + config=audio_config.InputAudioConfig( + audio_encoding=audio_config.AudioEncoding.AUDIO_ENCODING_LINEAR_16 + ) + ), + event_input=session.EventInput(name="name_value"), + ) + + +def test_streaming_analyze_content( + transport: str = "grpc", request_type=participant.StreamingAnalyzeContentRequest +): + client = ParticipantsClient( + 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_analyze_content), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iter([participant.StreamingAnalyzeContentResponse()]) + + response = client.streaming_analyze_content(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, participant.StreamingAnalyzeContentResponse) + + +def test_streaming_analyze_content_from_dict(): + test_streaming_analyze_content(request_type=dict) + + +@pytest.mark.asyncio +async def test_streaming_analyze_content_async( + transport: str = "grpc_asyncio", + request_type=participant.StreamingAnalyzeContentRequest, +): + client = ParticipantsAsyncClient( + 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_analyze_content), "__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=[participant.StreamingAnalyzeContentResponse()] + ) + + response = await client.streaming_analyze_content(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, participant.StreamingAnalyzeContentResponse) + + +@pytest.mark.asyncio +async def test_streaming_analyze_content_async_from_dict(): + await test_streaming_analyze_content_async(request_type=dict) + + +def test_suggest_articles( + transport: str = "grpc", request_type=participant.SuggestArticlesRequest +): + client = ParticipantsClient( + 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.suggest_articles), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestArticlesResponse( + latest_message="latest_message_value", context_size=1311, + ) + + response = client.suggest_articles(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestArticlesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, participant.SuggestArticlesResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +def test_suggest_articles_from_dict(): + test_suggest_articles(request_type=dict) + + +def test_suggest_articles_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.suggest_articles), "__call__") as call: + client.suggest_articles() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestArticlesRequest() + + +@pytest.mark.asyncio +async def test_suggest_articles_async( + transport: str = "grpc_asyncio", request_type=participant.SuggestArticlesRequest +): + client = ParticipantsAsyncClient( + 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.suggest_articles), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestArticlesResponse( + latest_message="latest_message_value", context_size=1311, + ) + ) + + response = await client.suggest_articles(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestArticlesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, participant.SuggestArticlesResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +@pytest.mark.asyncio +async def test_suggest_articles_async_from_dict(): + await test_suggest_articles_async(request_type=dict) + + +def test_suggest_articles_field_headers(): + client = ParticipantsClient(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 = participant.SuggestArticlesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.suggest_articles), "__call__") as call: + call.return_value = participant.SuggestArticlesResponse() + + client.suggest_articles(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_suggest_articles_field_headers_async(): + client = ParticipantsAsyncClient(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 = participant.SuggestArticlesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.suggest_articles), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestArticlesResponse() + ) + + await client.suggest_articles(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_suggest_articles_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.suggest_articles), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestArticlesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.suggest_articles(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_suggest_articles_flattened_error(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.suggest_articles( + participant.SuggestArticlesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_suggest_articles_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.suggest_articles), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestArticlesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestArticlesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.suggest_articles(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_suggest_articles_flattened_error_async(): + client = ParticipantsAsyncClient(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.suggest_articles( + participant.SuggestArticlesRequest(), parent="parent_value", + ) + + +def test_suggest_faq_answers( + transport: str = "grpc", request_type=participant.SuggestFaqAnswersRequest +): + client = ParticipantsClient( + 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.suggest_faq_answers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestFaqAnswersResponse( + latest_message="latest_message_value", context_size=1311, + ) + + response = client.suggest_faq_answers(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestFaqAnswersRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, participant.SuggestFaqAnswersResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +def test_suggest_faq_answers_from_dict(): + test_suggest_faq_answers(request_type=dict) + + +def test_suggest_faq_answers_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_faq_answers), "__call__" + ) as call: + client.suggest_faq_answers() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestFaqAnswersRequest() + + +@pytest.mark.asyncio +async def test_suggest_faq_answers_async( + transport: str = "grpc_asyncio", request_type=participant.SuggestFaqAnswersRequest +): + client = ParticipantsAsyncClient( + 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.suggest_faq_answers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestFaqAnswersResponse( + latest_message="latest_message_value", context_size=1311, + ) + ) + + response = await client.suggest_faq_answers(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestFaqAnswersRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, participant.SuggestFaqAnswersResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +@pytest.mark.asyncio +async def test_suggest_faq_answers_async_from_dict(): + await test_suggest_faq_answers_async(request_type=dict) + + +def test_suggest_faq_answers_field_headers(): + client = ParticipantsClient(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 = participant.SuggestFaqAnswersRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_faq_answers), "__call__" + ) as call: + call.return_value = participant.SuggestFaqAnswersResponse() + + client.suggest_faq_answers(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_suggest_faq_answers_field_headers_async(): + client = ParticipantsAsyncClient(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 = participant.SuggestFaqAnswersRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_faq_answers), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestFaqAnswersResponse() + ) + + await client.suggest_faq_answers(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_suggest_faq_answers_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_faq_answers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestFaqAnswersResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.suggest_faq_answers(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_suggest_faq_answers_flattened_error(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.suggest_faq_answers( + participant.SuggestFaqAnswersRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_suggest_faq_answers_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_faq_answers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestFaqAnswersResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestFaqAnswersResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.suggest_faq_answers(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_suggest_faq_answers_flattened_error_async(): + client = ParticipantsAsyncClient(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.suggest_faq_answers( + participant.SuggestFaqAnswersRequest(), parent="parent_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.ParticipantsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.ParticipantsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ParticipantsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.ParticipantsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ParticipantsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.ParticipantsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = ParticipantsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.ParticipantsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.ParticipantsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.ParticipantsGrpcTransport, + transports.ParticipantsGrpcAsyncIOTransport, + ], +) +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 = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.ParticipantsGrpcTransport,) + + +def test_participants_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.ParticipantsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_participants_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflow_v2.services.participants.transports.ParticipantsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.ParticipantsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "create_participant", + "get_participant", + "list_participants", + "update_participant", + "analyze_content", + "streaming_analyze_content", + "suggest_articles", + "suggest_faq_answers", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_participants_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.dialogflow_v2.services.participants.transports.ParticipantsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ParticipantsTransport( + 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_participants_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.dialogflow_v2.services.participants.transports.ParticipantsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ParticipantsTransport() + adc.assert_called_once() + + +def test_participants_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) + ParticipantsClient() + 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_participants_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.ParticipantsGrpcTransport( + 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", + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.ParticipantsGrpcTransport, transports.ParticipantsGrpcAsyncIOTransport], +) +def test_participants_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_participants_host_no_port(): + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_participants_host_with_port(): + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_participants_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ParticipantsGrpcTransport( + 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_participants_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ParticipantsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.ParticipantsGrpcTransport, transports.ParticipantsGrpcAsyncIOTransport], +) +def test_participants_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.ParticipantsGrpcTransport, transports.ParticipantsGrpcAsyncIOTransport], +) +def test_participants_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_context_path(): + project = "squid" + session = "clam" + context = "whelk" + + expected = "projects/{project}/agent/sessions/{session}/contexts/{context}".format( + project=project, session=session, context=context, + ) + actual = ParticipantsClient.context_path(project, session, context) + assert expected == actual + + +def test_parse_context_path(): + expected = { + "project": "octopus", + "session": "oyster", + "context": "nudibranch", + } + path = ParticipantsClient.context_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_context_path(path) + assert expected == actual + + +def test_intent_path(): + project = "cuttlefish" + intent = "mussel" + + expected = "projects/{project}/agent/intents/{intent}".format( + project=project, intent=intent, + ) + actual = ParticipantsClient.intent_path(project, intent) + assert expected == actual + + +def test_parse_intent_path(): + expected = { + "project": "winkle", + "intent": "nautilus", + } + path = ParticipantsClient.intent_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_intent_path(path) + assert expected == actual + + +def test_message_path(): + project = "scallop" + conversation = "abalone" + message = "squid" + + expected = "projects/{project}/conversations/{conversation}/messages/{message}".format( + project=project, conversation=conversation, message=message, + ) + actual = ParticipantsClient.message_path(project, conversation, message) + assert expected == actual + + +def test_parse_message_path(): + expected = { + "project": "clam", + "conversation": "whelk", + "message": "octopus", + } + path = ParticipantsClient.message_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_message_path(path) + assert expected == actual + + +def test_participant_path(): + project = "oyster" + conversation = "nudibranch" + participant = "cuttlefish" + + expected = "projects/{project}/conversations/{conversation}/participants/{participant}".format( + project=project, conversation=conversation, participant=participant, + ) + actual = ParticipantsClient.participant_path(project, conversation, participant) + assert expected == actual + + +def test_parse_participant_path(): + expected = { + "project": "mussel", + "conversation": "winkle", + "participant": "nautilus", + } + path = ParticipantsClient.participant_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_participant_path(path) + assert expected == actual + + +def test_session_entity_type_path(): + project = "scallop" + session = "abalone" + entity_type = "squid" + + expected = "projects/{project}/agent/sessions/{session}/entityTypes/{entity_type}".format( + project=project, session=session, entity_type=entity_type, + ) + actual = ParticipantsClient.session_entity_type_path(project, session, entity_type) + assert expected == actual + + +def test_parse_session_entity_type_path(): + expected = { + "project": "clam", + "session": "whelk", + "entity_type": "octopus", + } + path = ParticipantsClient.session_entity_type_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_session_entity_type_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "oyster" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = ParticipantsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nudibranch", + } + path = ParticipantsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "cuttlefish" + + expected = "folders/{folder}".format(folder=folder,) + actual = ParticipantsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "mussel", + } + path = ParticipantsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "winkle" + + expected = "organizations/{organization}".format(organization=organization,) + actual = ParticipantsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nautilus", + } + path = ParticipantsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "scallop" + + expected = "projects/{project}".format(project=project,) + actual = ParticipantsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "abalone", + } + path = ParticipantsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.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 = ParticipantsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "whelk", + "location": "octopus", + } + path = ParticipantsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.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.ParticipantsTransport, "_prep_wrapped_messages" + ) as prep: + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.ParticipantsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = ParticipantsClient.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/dialogflow_v2/test_session_entity_types.py b/tests/unit/gapic/dialogflow_v2/test_session_entity_types.py index 2b87fbb58..611435246 100644 --- a/tests/unit/gapic/dialogflow_v2/test_session_entity_types.py +++ b/tests/unit/gapic/dialogflow_v2/test_session_entity_types.py @@ -95,7 +95,24 @@ def test__get_default_mtls_endpoint(): @pytest.mark.parametrize( - "client_class", [SessionEntityTypesClient, SessionEntityTypesAsyncClient] + "client_class", [SessionEntityTypesClient, SessionEntityTypesAsyncClient,] +) +def test_session_entity_types_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize( + "client_class", [SessionEntityTypesClient, SessionEntityTypesAsyncClient,] ) def test_session_entity_types_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() @@ -105,16 +122,21 @@ def test_session_entity_types_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_session_entity_types_client_get_transport_class(): transport = SessionEntityTypesClient.get_transport_class() - assert transport == transports.SessionEntityTypesGrpcTransport + available_transports = [ + transports.SessionEntityTypesGrpcTransport, + ] + assert transport in available_transports transport = SessionEntityTypesClient.get_transport_class("grpc") assert transport == transports.SessionEntityTypesGrpcTransport @@ -165,7 +187,7 @@ def test_session_entity_types_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -181,7 +203,7 @@ def test_session_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -197,7 +219,7 @@ def test_session_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -225,7 +247,7 @@ def test_session_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -286,29 +308,25 @@ def test_session_entity_types_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -317,66 +335,53 @@ def test_session_entity_types_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -402,7 +407,7 @@ def test_session_entity_types_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -432,7 +437,7 @@ def test_session_entity_types_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -451,7 +456,7 @@ def test_session_entity_types_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -497,6 +502,24 @@ def test_list_session_entity_types_from_dict(): test_list_session_entity_types(request_type=dict) +def test_list_session_entity_types_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_session_entity_types), "__call__" + ) as call: + client.list_session_entity_types() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.ListSessionEntityTypesRequest() + + @pytest.mark.asyncio async def test_list_session_entity_types_async( transport: str = "grpc_asyncio", @@ -896,6 +919,24 @@ def test_get_session_entity_type_from_dict(): test_get_session_entity_type(request_type=dict) +def test_get_session_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_session_entity_type), "__call__" + ) as call: + client.get_session_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.GetSessionEntityTypeRequest() + + @pytest.mark.asyncio async def test_get_session_entity_type_async( transport: str = "grpc_asyncio", @@ -1123,6 +1164,24 @@ def test_create_session_entity_type_from_dict(): test_create_session_entity_type(request_type=dict) +def test_create_session_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.create_session_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_session_entity_type.CreateSessionEntityTypeRequest() + + @pytest.mark.asyncio async def test_create_session_entity_type_async( transport: str = "grpc_asyncio", @@ -1376,6 +1435,24 @@ def test_update_session_entity_type_from_dict(): test_update_session_entity_type(request_type=dict) +def test_update_session_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.update_session_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_session_entity_type.UpdateSessionEntityTypeRequest() + + @pytest.mark.asyncio async def test_update_session_entity_type_async( transport: str = "grpc_asyncio", @@ -1624,6 +1701,24 @@ def test_delete_session_entity_type_from_dict(): test_delete_session_entity_type(request_type=dict) +def test_delete_session_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.delete_session_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.DeleteSessionEntityTypeRequest() + + @pytest.mark.asyncio async def test_delete_session_entity_type_async( transport: str = "grpc_asyncio", @@ -1961,6 +2056,56 @@ def test_session_entity_types_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.SessionEntityTypesGrpcTransport, + transports.SessionEntityTypesGrpcAsyncIOTransport, + ], +) +def test_session_entity_types_grpc_transport_client_cert_source_for_mtls( + transport_class, +): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_session_entity_types_host_no_port(): client = SessionEntityTypesClient( credentials=credentials.AnonymousCredentials(), @@ -1982,7 +2127,7 @@ def test_session_entity_types_host_with_port(): def test_session_entity_types_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SessionEntityTypesGrpcTransport( @@ -1994,7 +2139,7 @@ def test_session_entity_types_grpc_transport_channel(): def test_session_entity_types_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SessionEntityTypesGrpcAsyncIOTransport( @@ -2005,6 +2150,8 @@ def test_session_entity_types_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [ @@ -2019,7 +2166,7 @@ def test_session_entity_types_transport_channel_mtls_with_client_cert_source( "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -2060,6 +2207,8 @@ def test_session_entity_types_transport_channel_mtls_with_client_cert_source( assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [ @@ -2075,7 +2224,7 @@ def test_session_entity_types_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2/test_sessions.py b/tests/unit/gapic/dialogflow_v2/test_sessions.py index c7c891023..8b119b26a 100644 --- a/tests/unit/gapic/dialogflow_v2/test_sessions.py +++ b/tests/unit/gapic/dialogflow_v2/test_sessions.py @@ -87,7 +87,22 @@ def test__get_default_mtls_endpoint(): assert SessionsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [SessionsClient, SessionsAsyncClient]) +@pytest.mark.parametrize("client_class", [SessionsClient, SessionsAsyncClient,]) +def test_sessions_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [SessionsClient, SessionsAsyncClient,]) def test_sessions_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -96,16 +111,21 @@ def test_sessions_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_sessions_client_get_transport_class(): transport = SessionsClient.get_transport_class() - assert transport == transports.SessionsGrpcTransport + available_transports = [ + transports.SessionsGrpcTransport, + ] + assert transport in available_transports transport = SessionsClient.get_transport_class("grpc") assert transport == transports.SessionsGrpcTransport @@ -148,7 +168,7 @@ def test_sessions_client_client_options(client_class, transport_class, transport credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -164,7 +184,7 @@ def test_sessions_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -180,7 +200,7 @@ def test_sessions_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -208,7 +228,7 @@ def test_sessions_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -257,29 +277,25 @@ def test_sessions_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -288,66 +304,53 @@ def test_sessions_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -369,7 +372,7 @@ def test_sessions_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -395,7 +398,7 @@ def test_sessions_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -412,7 +415,7 @@ def test_sessions_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -457,6 +460,22 @@ def test_detect_intent_from_dict(): test_detect_intent(request_type=dict) +def test_detect_intent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.detect_intent), "__call__") as call: + client.detect_intent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_session.DetectIntentRequest() + + @pytest.mark.asyncio async def test_detect_intent_async( transport: str = "grpc_asyncio", request_type=gcd_session.DetectIntentRequest @@ -790,7 +809,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], + [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -900,6 +919,51 @@ def test_sessions_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], +) +def test_sessions_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_sessions_host_no_port(): client = SessionsClient( credentials=credentials.AnonymousCredentials(), @@ -921,7 +985,7 @@ def test_sessions_host_with_port(): def test_sessions_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SessionsGrpcTransport( @@ -933,7 +997,7 @@ def test_sessions_grpc_transport_channel(): def test_sessions_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SessionsGrpcAsyncIOTransport( @@ -944,6 +1008,8 @@ def test_sessions_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], @@ -953,7 +1019,7 @@ def test_sessions_transport_channel_mtls_with_client_cert_source(transport_class "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -994,6 +1060,8 @@ def test_sessions_transport_channel_mtls_with_client_cert_source(transport_class assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], @@ -1006,7 +1074,7 @@ def test_sessions_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2beta1/__init__.py b/tests/unit/gapic/dialogflow_v2beta1/__init__.py index 8b1378917..42ffdf2bc 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/__init__.py +++ b/tests/unit/gapic/dialogflow_v2beta1/__init__.py @@ -1 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# diff --git a/tests/unit/gapic/dialogflow_v2beta1/test_agents.py b/tests/unit/gapic/dialogflow_v2beta1/test_agents.py index 653647d6f..ec733fb0b 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/test_agents.py +++ b/tests/unit/gapic/dialogflow_v2beta1/test_agents.py @@ -86,7 +86,22 @@ def test__get_default_mtls_endpoint(): assert AgentsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [AgentsClient, AgentsAsyncClient]) +@pytest.mark.parametrize("client_class", [AgentsClient, AgentsAsyncClient,]) +def test_agents_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [AgentsClient, AgentsAsyncClient,]) def test_agents_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -95,16 +110,21 @@ def test_agents_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_agents_client_get_transport_class(): transport = AgentsClient.get_transport_class() - assert transport == transports.AgentsGrpcTransport + available_transports = [ + transports.AgentsGrpcTransport, + ] + assert transport in available_transports transport = AgentsClient.get_transport_class("grpc") assert transport == transports.AgentsGrpcTransport @@ -145,7 +165,7 @@ def test_agents_client_client_options(client_class, transport_class, transport_n credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -161,7 +181,7 @@ def test_agents_client_client_options(client_class, transport_class, transport_n credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -177,7 +197,7 @@ def test_agents_client_client_options(client_class, transport_class, transport_n credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -205,7 +225,7 @@ def test_agents_client_client_options(client_class, transport_class, transport_n credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -252,29 +272,25 @@ def test_agents_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -283,66 +299,53 @@ def test_agents_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -364,7 +367,7 @@ def test_agents_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -390,7 +393,7 @@ def test_agents_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -407,7 +410,7 @@ def test_agents_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -481,6 +484,22 @@ def test_get_agent_from_dict(): test_get_agent(request_type=dict) +def test_get_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_agent), "__call__") as call: + client.get_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.GetAgentRequest() + + @pytest.mark.asyncio async def test_get_agent_async( transport: str = "grpc_asyncio", request_type=agent.GetAgentRequest @@ -736,6 +755,22 @@ def test_set_agent_from_dict(): test_set_agent(request_type=dict) +def test_set_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_agent), "__call__") as call: + client.set_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_agent.SetAgentRequest() + + @pytest.mark.asyncio async def test_set_agent_async( transport: str = "grpc_asyncio", request_type=gcd_agent.SetAgentRequest @@ -957,6 +992,22 @@ def test_delete_agent_from_dict(): test_delete_agent(request_type=dict) +def test_delete_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_agent), "__call__") as call: + client.delete_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.DeleteAgentRequest() + + @pytest.mark.asyncio async def test_delete_agent_async( transport: str = "grpc_asyncio", request_type=agent.DeleteAgentRequest @@ -1140,6 +1191,22 @@ def test_search_agents_from_dict(): test_search_agents(request_type=dict) +def test_search_agents_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.search_agents), "__call__") as call: + client.search_agents() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.SearchAgentsRequest() + + @pytest.mark.asyncio async def test_search_agents_async( transport: str = "grpc_asyncio", request_type=agent.SearchAgentsRequest @@ -1432,6 +1499,22 @@ def test_train_agent_from_dict(): test_train_agent(request_type=dict) +def test_train_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.train_agent), "__call__") as call: + client.train_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.TrainAgentRequest() + + @pytest.mark.asyncio async def test_train_agent_async( transport: str = "grpc_asyncio", request_type=agent.TrainAgentRequest @@ -1616,6 +1699,22 @@ def test_export_agent_from_dict(): test_export_agent(request_type=dict) +def test_export_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.export_agent), "__call__") as call: + client.export_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.ExportAgentRequest() + + @pytest.mark.asyncio async def test_export_agent_async( transport: str = "grpc_asyncio", request_type=agent.ExportAgentRequest @@ -1800,6 +1899,22 @@ def test_import_agent_from_dict(): test_import_agent(request_type=dict) +def test_import_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.import_agent), "__call__") as call: + client.import_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.ImportAgentRequest() + + @pytest.mark.asyncio async def test_import_agent_async( transport: str = "grpc_asyncio", request_type=agent.ImportAgentRequest @@ -1917,6 +2032,22 @@ def test_restore_agent_from_dict(): test_restore_agent(request_type=dict) +def test_restore_agent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.restore_agent), "__call__") as call: + client.restore_agent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.RestoreAgentRequest() + + @pytest.mark.asyncio async def test_restore_agent_async( transport: str = "grpc_asyncio", request_type=agent.RestoreAgentRequest @@ -2039,6 +2170,24 @@ def test_get_validation_result_from_dict(): test_get_validation_result(request_type=dict) +def test_get_validation_result_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AgentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_validation_result), "__call__" + ) as call: + client.get_validation_result() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == agent.GetValidationResultRequest() + + @pytest.mark.asyncio async def test_get_validation_result_async( transport: str = "grpc_asyncio", request_type=agent.GetValidationResultRequest @@ -2188,7 +2337,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], + [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -2310,6 +2459,51 @@ def test_agents_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], +) +def test_agents_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_agents_host_no_port(): client = AgentsClient( credentials=credentials.AnonymousCredentials(), @@ -2331,7 +2525,7 @@ def test_agents_host_with_port(): def test_agents_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.AgentsGrpcTransport( @@ -2343,7 +2537,7 @@ def test_agents_grpc_transport_channel(): def test_agents_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.AgentsGrpcAsyncIOTransport( @@ -2354,6 +2548,8 @@ def test_agents_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], @@ -2363,7 +2559,7 @@ def test_agents_transport_channel_mtls_with_client_cert_source(transport_class): "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -2404,6 +2600,8 @@ def test_agents_transport_channel_mtls_with_client_cert_source(transport_class): assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.AgentsGrpcTransport, transports.AgentsGrpcAsyncIOTransport], @@ -2416,7 +2614,7 @@ def test_agents_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2beta1/test_answer_records.py b/tests/unit/gapic/dialogflow_v2beta1/test_answer_records.py new file mode 100644 index 000000000..06408100c --- /dev/null +++ b/tests/unit/gapic/dialogflow_v2beta1/test_answer_records.py @@ -0,0 +1,1756 @@ +# -*- 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.dialogflow_v2beta1.services.answer_records import ( + AnswerRecordsAsyncClient, +) +from google.cloud.dialogflow_v2beta1.services.answer_records import AnswerRecordsClient +from google.cloud.dialogflow_v2beta1.services.answer_records import pagers +from google.cloud.dialogflow_v2beta1.services.answer_records import transports +from google.cloud.dialogflow_v2beta1.types import answer_record +from google.cloud.dialogflow_v2beta1.types import answer_record as gcd_answer_record +from google.cloud.dialogflow_v2beta1.types import participant +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # 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 AnswerRecordsClient._get_default_mtls_endpoint(None) is None + assert ( + AnswerRecordsClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + AnswerRecordsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + AnswerRecordsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + AnswerRecordsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + AnswerRecordsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [AnswerRecordsClient, AnswerRecordsAsyncClient,] +) +def test_answer_records_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize( + "client_class", [AnswerRecordsClient, AnswerRecordsAsyncClient,] +) +def test_answer_records_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 + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_answer_records_client_get_transport_class(): + transport = AnswerRecordsClient.get_transport_class() + available_transports = [ + transports.AnswerRecordsGrpcTransport, + ] + assert transport in available_transports + + transport = AnswerRecordsClient.get_transport_class("grpc") + assert transport == transports.AnswerRecordsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (AnswerRecordsClient, transports.AnswerRecordsGrpcTransport, "grpc"), + ( + AnswerRecordsAsyncClient, + transports.AnswerRecordsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + AnswerRecordsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(AnswerRecordsClient), +) +@mock.patch.object( + AnswerRecordsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(AnswerRecordsAsyncClient), +) +def test_answer_records_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(AnswerRecordsClient, "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(AnswerRecordsClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (AnswerRecordsClient, transports.AnswerRecordsGrpcTransport, "grpc", "true"), + ( + AnswerRecordsAsyncClient, + transports.AnswerRecordsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (AnswerRecordsClient, transports.AnswerRecordsGrpcTransport, "grpc", "false"), + ( + AnswerRecordsAsyncClient, + transports.AnswerRecordsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + AnswerRecordsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(AnswerRecordsClient), +) +@mock.patch.object( + AnswerRecordsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(AnswerRecordsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_answer_records_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (AnswerRecordsClient, transports.AnswerRecordsGrpcTransport, "grpc"), + ( + AnswerRecordsAsyncClient, + transports.AnswerRecordsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_answer_records_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (AnswerRecordsClient, transports.AnswerRecordsGrpcTransport, "grpc"), + ( + AnswerRecordsAsyncClient, + transports.AnswerRecordsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_answer_records_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_answer_records_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflow_v2beta1.services.answer_records.transports.AnswerRecordsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = AnswerRecordsClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_get_answer_record( + transport: str = "grpc", request_type=answer_record.GetAnswerRecordRequest +): + client = AnswerRecordsClient( + 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_answer_record), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = answer_record.AnswerRecord( + name="name_value", + agent_assistant_record=answer_record.AgentAssistantRecord( + article_suggestion_answer=participant.ArticleAnswer(title="title_value") + ), + ) + + response = client.get_answer_record(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == answer_record.GetAnswerRecordRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, answer_record.AnswerRecord) + + assert response.name == "name_value" + + +def test_get_answer_record_from_dict(): + test_get_answer_record(request_type=dict) + + +def test_get_answer_record_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_answer_record), "__call__" + ) as call: + client.get_answer_record() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == answer_record.GetAnswerRecordRequest() + + +@pytest.mark.asyncio +async def test_get_answer_record_async( + transport: str = "grpc_asyncio", request_type=answer_record.GetAnswerRecordRequest +): + client = AnswerRecordsAsyncClient( + 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_answer_record), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + answer_record.AnswerRecord(name="name_value",) + ) + + response = await client.get_answer_record(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == answer_record.GetAnswerRecordRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, answer_record.AnswerRecord) + + assert response.name == "name_value" + + +@pytest.mark.asyncio +async def test_get_answer_record_async_from_dict(): + await test_get_answer_record_async(request_type=dict) + + +def test_get_answer_record_field_headers(): + client = AnswerRecordsClient(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 = answer_record.GetAnswerRecordRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_answer_record), "__call__" + ) as call: + call.return_value = answer_record.AnswerRecord() + + client.get_answer_record(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_answer_record_field_headers_async(): + client = AnswerRecordsAsyncClient(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 = answer_record.GetAnswerRecordRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_answer_record), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + answer_record.AnswerRecord() + ) + + await client.get_answer_record(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_list_answer_records( + transport: str = "grpc", request_type=answer_record.ListAnswerRecordsRequest +): + client = AnswerRecordsClient( + 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_answer_records), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = answer_record.ListAnswerRecordsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_answer_records(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == answer_record.ListAnswerRecordsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListAnswerRecordsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_answer_records_from_dict(): + test_list_answer_records(request_type=dict) + + +def test_list_answer_records_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + client.list_answer_records() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == answer_record.ListAnswerRecordsRequest() + + +@pytest.mark.asyncio +async def test_list_answer_records_async( + transport: str = "grpc_asyncio", request_type=answer_record.ListAnswerRecordsRequest +): + client = AnswerRecordsAsyncClient( + 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_answer_records), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + answer_record.ListAnswerRecordsResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_answer_records(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == answer_record.ListAnswerRecordsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListAnswerRecordsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_answer_records_async_from_dict(): + await test_list_answer_records_async(request_type=dict) + + +def test_list_answer_records_field_headers(): + client = AnswerRecordsClient(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 = answer_record.ListAnswerRecordsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + call.return_value = answer_record.ListAnswerRecordsResponse() + + client.list_answer_records(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_answer_records_field_headers_async(): + client = AnswerRecordsAsyncClient(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 = answer_record.ListAnswerRecordsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + answer_record.ListAnswerRecordsResponse() + ) + + await client.list_answer_records(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_answer_records_flattened(): + client = AnswerRecordsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = answer_record.ListAnswerRecordsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_answer_records(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_answer_records_flattened_error(): + client = AnswerRecordsClient(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_answer_records( + answer_record.ListAnswerRecordsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_answer_records_flattened_async(): + client = AnswerRecordsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = answer_record.ListAnswerRecordsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + answer_record.ListAnswerRecordsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_answer_records(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_answer_records_flattened_error_async(): + client = AnswerRecordsAsyncClient(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_answer_records( + answer_record.ListAnswerRecordsRequest(), parent="parent_value", + ) + + +def test_list_answer_records_pager(): + client = AnswerRecordsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + next_page_token="abc", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[], next_page_token="def", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[answer_record.AnswerRecord(),], next_page_token="ghi", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_answer_records(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, answer_record.AnswerRecord) for i in results) + + +def test_list_answer_records_pages(): + client = AnswerRecordsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + next_page_token="abc", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[], next_page_token="def", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[answer_record.AnswerRecord(),], next_page_token="ghi", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + ), + RuntimeError, + ) + pages = list(client.list_answer_records(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_answer_records_async_pager(): + client = AnswerRecordsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + next_page_token="abc", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[], next_page_token="def", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[answer_record.AnswerRecord(),], next_page_token="ghi", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + ), + RuntimeError, + ) + async_pager = await client.list_answer_records(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, answer_record.AnswerRecord) for i in responses) + + +@pytest.mark.asyncio +async def test_list_answer_records_async_pages(): + client = AnswerRecordsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_answer_records), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + next_page_token="abc", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[], next_page_token="def", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[answer_record.AnswerRecord(),], next_page_token="ghi", + ), + answer_record.ListAnswerRecordsResponse( + answer_records=[ + answer_record.AnswerRecord(), + answer_record.AnswerRecord(), + ], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_answer_records(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_update_answer_record( + transport: str = "grpc", request_type=gcd_answer_record.UpdateAnswerRecordRequest +): + client = AnswerRecordsClient( + 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_answer_record), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_answer_record.AnswerRecord( + name="name_value", + agent_assistant_record=gcd_answer_record.AgentAssistantRecord( + article_suggestion_answer=participant.ArticleAnswer(title="title_value") + ), + ) + + response = client.update_answer_record(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_answer_record.UpdateAnswerRecordRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_answer_record.AnswerRecord) + + assert response.name == "name_value" + + +def test_update_answer_record_from_dict(): + test_update_answer_record(request_type=dict) + + +def test_update_answer_record_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_answer_record), "__call__" + ) as call: + client.update_answer_record() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_answer_record.UpdateAnswerRecordRequest() + + +@pytest.mark.asyncio +async def test_update_answer_record_async( + transport: str = "grpc_asyncio", + request_type=gcd_answer_record.UpdateAnswerRecordRequest, +): + client = AnswerRecordsAsyncClient( + 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_answer_record), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_answer_record.AnswerRecord(name="name_value",) + ) + + response = await client.update_answer_record(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_answer_record.UpdateAnswerRecordRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_answer_record.AnswerRecord) + + assert response.name == "name_value" + + +@pytest.mark.asyncio +async def test_update_answer_record_async_from_dict(): + await test_update_answer_record_async(request_type=dict) + + +def test_update_answer_record_field_headers(): + client = AnswerRecordsClient(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 = gcd_answer_record.UpdateAnswerRecordRequest() + request.answer_record.name = "answer_record.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_answer_record), "__call__" + ) as call: + call.return_value = gcd_answer_record.AnswerRecord() + + client.update_answer_record(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", + "answer_record.name=answer_record.name/value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_answer_record_field_headers_async(): + client = AnswerRecordsAsyncClient(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 = gcd_answer_record.UpdateAnswerRecordRequest() + request.answer_record.name = "answer_record.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_answer_record), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_answer_record.AnswerRecord() + ) + + await client.update_answer_record(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", + "answer_record.name=answer_record.name/value", + ) in kw["metadata"] + + +def test_update_answer_record_flattened(): + client = AnswerRecordsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_answer_record), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_answer_record.AnswerRecord() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_answer_record( + answer_record=gcd_answer_record.AnswerRecord(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].answer_record == gcd_answer_record.AnswerRecord( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_answer_record_flattened_error(): + client = AnswerRecordsClient(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_answer_record( + gcd_answer_record.UpdateAnswerRecordRequest(), + answer_record=gcd_answer_record.AnswerRecord(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_answer_record_flattened_async(): + client = AnswerRecordsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_answer_record), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_answer_record.AnswerRecord() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_answer_record.AnswerRecord() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_answer_record( + answer_record=gcd_answer_record.AnswerRecord(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].answer_record == gcd_answer_record.AnswerRecord( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_answer_record_flattened_error_async(): + client = AnswerRecordsAsyncClient(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_answer_record( + gcd_answer_record.UpdateAnswerRecordRequest(), + answer_record=gcd_answer_record.AnswerRecord(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.AnswerRecordsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.AnswerRecordsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = AnswerRecordsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.AnswerRecordsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = AnswerRecordsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.AnswerRecordsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = AnswerRecordsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.AnswerRecordsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.AnswerRecordsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.AnswerRecordsGrpcTransport, + transports.AnswerRecordsGrpcAsyncIOTransport, + ], +) +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 = AnswerRecordsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.AnswerRecordsGrpcTransport,) + + +def test_answer_records_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.AnswerRecordsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_answer_records_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflow_v2beta1.services.answer_records.transports.AnswerRecordsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.AnswerRecordsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "get_answer_record", + "list_answer_records", + "update_answer_record", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_answer_records_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.dialogflow_v2beta1.services.answer_records.transports.AnswerRecordsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.AnswerRecordsTransport( + 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_answer_records_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.dialogflow_v2beta1.services.answer_records.transports.AnswerRecordsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.AnswerRecordsTransport() + adc.assert_called_once() + + +def test_answer_records_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) + AnswerRecordsClient() + 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_answer_records_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.AnswerRecordsGrpcTransport( + 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", + ) + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.AnswerRecordsGrpcTransport, + transports.AnswerRecordsGrpcAsyncIOTransport, + ], +) +def test_answer_records_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_answer_records_host_no_port(): + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_answer_records_host_with_port(): + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_answer_records_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.AnswerRecordsGrpcTransport( + 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_answer_records_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.AnswerRecordsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.AnswerRecordsGrpcTransport, + transports.AnswerRecordsGrpcAsyncIOTransport, + ], +) +def test_answer_records_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.AnswerRecordsGrpcTransport, + transports.AnswerRecordsGrpcAsyncIOTransport, + ], +) +def test_answer_records_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_answer_record_path(): + project = "squid" + answer_record = "clam" + + expected = "projects/{project}/answerRecords/{answer_record}".format( + project=project, answer_record=answer_record, + ) + actual = AnswerRecordsClient.answer_record_path(project, answer_record) + assert expected == actual + + +def test_parse_answer_record_path(): + expected = { + "project": "whelk", + "answer_record": "octopus", + } + path = AnswerRecordsClient.answer_record_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.parse_answer_record_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "oyster" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = AnswerRecordsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nudibranch", + } + path = AnswerRecordsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "cuttlefish" + + expected = "folders/{folder}".format(folder=folder,) + actual = AnswerRecordsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "mussel", + } + path = AnswerRecordsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "winkle" + + expected = "organizations/{organization}".format(organization=organization,) + actual = AnswerRecordsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nautilus", + } + path = AnswerRecordsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "scallop" + + expected = "projects/{project}".format(project=project,) + actual = AnswerRecordsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "abalone", + } + path = AnswerRecordsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.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 = AnswerRecordsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "whelk", + "location": "octopus", + } + path = AnswerRecordsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = AnswerRecordsClient.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.AnswerRecordsTransport, "_prep_wrapped_messages" + ) as prep: + client = AnswerRecordsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.AnswerRecordsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = AnswerRecordsClient.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/dialogflow_v2beta1/test_contexts.py b/tests/unit/gapic/dialogflow_v2beta1/test_contexts.py index 4a02a8808..dcdec7af4 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/test_contexts.py +++ b/tests/unit/gapic/dialogflow_v2beta1/test_contexts.py @@ -82,7 +82,22 @@ def test__get_default_mtls_endpoint(): assert ContextsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [ContextsClient, ContextsAsyncClient]) +@pytest.mark.parametrize("client_class", [ContextsClient, ContextsAsyncClient,]) +def test_contexts_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [ContextsClient, ContextsAsyncClient,]) def test_contexts_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -91,16 +106,21 @@ def test_contexts_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_contexts_client_get_transport_class(): transport = ContextsClient.get_transport_class() - assert transport == transports.ContextsGrpcTransport + available_transports = [ + transports.ContextsGrpcTransport, + ] + assert transport in available_transports transport = ContextsClient.get_transport_class("grpc") assert transport == transports.ContextsGrpcTransport @@ -143,7 +163,7 @@ def test_contexts_client_client_options(client_class, transport_class, transport credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -159,7 +179,7 @@ def test_contexts_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -175,7 +195,7 @@ def test_contexts_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -203,7 +223,7 @@ def test_contexts_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -252,29 +272,25 @@ def test_contexts_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -283,66 +299,53 @@ def test_contexts_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -364,7 +367,7 @@ def test_contexts_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -390,7 +393,7 @@ def test_contexts_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -407,7 +410,7 @@ def test_contexts_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -450,6 +453,22 @@ def test_list_contexts_from_dict(): test_list_contexts(request_type=dict) +def test_list_contexts_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_contexts), "__call__") as call: + client.list_contexts() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == context.ListContextsRequest() + + @pytest.mark.asyncio async def test_list_contexts_async( transport: str = "grpc_asyncio", request_type=context.ListContextsRequest @@ -763,6 +782,22 @@ def test_get_context_from_dict(): test_get_context(request_type=dict) +def test_get_context_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_context), "__call__") as call: + client.get_context() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == context.GetContextRequest() + + @pytest.mark.asyncio async def test_get_context_async( transport: str = "grpc_asyncio", request_type=context.GetContextRequest @@ -954,6 +989,22 @@ def test_create_context_from_dict(): test_create_context(request_type=dict) +def test_create_context_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_context), "__call__") as call: + client.create_context() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_context.CreateContextRequest() + + @pytest.mark.asyncio async def test_create_context_async( transport: str = "grpc_asyncio", request_type=gcd_context.CreateContextRequest @@ -1157,6 +1208,22 @@ def test_update_context_from_dict(): test_update_context(request_type=dict) +def test_update_context_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_context), "__call__") as call: + client.update_context() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_context.UpdateContextRequest() + + @pytest.mark.asyncio async def test_update_context_async( transport: str = "grpc_asyncio", request_type=gcd_context.UpdateContextRequest @@ -1361,6 +1428,22 @@ def test_delete_context_from_dict(): test_delete_context(request_type=dict) +def test_delete_context_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_context), "__call__") as call: + client.delete_context() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == context.DeleteContextRequest() + + @pytest.mark.asyncio async def test_delete_context_async( transport: str = "grpc_asyncio", request_type=context.DeleteContextRequest @@ -1543,6 +1626,24 @@ def test_delete_all_contexts_from_dict(): test_delete_all_contexts(request_type=dict) +def test_delete_all_contexts_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ContextsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_all_contexts), "__call__" + ) as call: + client.delete_all_contexts() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == context.DeleteAllContextsRequest() + + @pytest.mark.asyncio async def test_delete_all_contexts_async( transport: str = "grpc_asyncio", request_type=context.DeleteAllContextsRequest @@ -1757,7 +1858,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.ContextsGrpcTransport, transports.ContextsGrpcAsyncIOTransport], + [transports.ContextsGrpcTransport, transports.ContextsGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -1871,6 +1972,51 @@ def test_contexts_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.ContextsGrpcTransport, transports.ContextsGrpcAsyncIOTransport], +) +def test_contexts_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_contexts_host_no_port(): client = ContextsClient( credentials=credentials.AnonymousCredentials(), @@ -1892,7 +2038,7 @@ def test_contexts_host_with_port(): def test_contexts_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.ContextsGrpcTransport( @@ -1904,7 +2050,7 @@ def test_contexts_grpc_transport_channel(): def test_contexts_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.ContextsGrpcAsyncIOTransport( @@ -1915,6 +2061,8 @@ def test_contexts_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.ContextsGrpcTransport, transports.ContextsGrpcAsyncIOTransport], @@ -1924,7 +2072,7 @@ def test_contexts_transport_channel_mtls_with_client_cert_source(transport_class "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -1965,6 +2113,8 @@ def test_contexts_transport_channel_mtls_with_client_cert_source(transport_class assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.ContextsGrpcTransport, transports.ContextsGrpcAsyncIOTransport], @@ -1977,7 +2127,7 @@ def test_contexts_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2beta1/test_conversation_profiles.py b/tests/unit/gapic/dialogflow_v2beta1/test_conversation_profiles.py new file mode 100644 index 000000000..8aa9c1358 --- /dev/null +++ b/tests/unit/gapic/dialogflow_v2beta1/test_conversation_profiles.py @@ -0,0 +1,2494 @@ +# -*- 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.dialogflow_v2beta1.services.conversation_profiles import ( + ConversationProfilesAsyncClient, +) +from google.cloud.dialogflow_v2beta1.services.conversation_profiles import ( + ConversationProfilesClient, +) +from google.cloud.dialogflow_v2beta1.services.conversation_profiles import pagers +from google.cloud.dialogflow_v2beta1.services.conversation_profiles import transports +from google.cloud.dialogflow_v2beta1.types import audio_config +from google.cloud.dialogflow_v2beta1.types import conversation_profile +from google.cloud.dialogflow_v2beta1.types import ( + conversation_profile as gcd_conversation_profile, +) +from google.cloud.dialogflow_v2beta1.types import participant +from google.oauth2 import service_account +from google.protobuf import field_mask_pb2 as field_mask # 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 ConversationProfilesClient._get_default_mtls_endpoint(None) is None + assert ( + ConversationProfilesClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + ConversationProfilesClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + ConversationProfilesClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ConversationProfilesClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ConversationProfilesClient._get_default_mtls_endpoint(non_googleapi) + == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [ConversationProfilesClient, ConversationProfilesAsyncClient,] +) +def test_conversation_profiles_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize( + "client_class", [ConversationProfilesClient, ConversationProfilesAsyncClient,] +) +def test_conversation_profiles_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 + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_conversation_profiles_client_get_transport_class(): + transport = ConversationProfilesClient.get_transport_class() + available_transports = [ + transports.ConversationProfilesGrpcTransport, + ] + assert transport in available_transports + + transport = ConversationProfilesClient.get_transport_class("grpc") + assert transport == transports.ConversationProfilesGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + ConversationProfilesClient, + transports.ConversationProfilesGrpcTransport, + "grpc", + ), + ( + ConversationProfilesAsyncClient, + transports.ConversationProfilesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + ConversationProfilesClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationProfilesClient), +) +@mock.patch.object( + ConversationProfilesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationProfilesAsyncClient), +) +def test_conversation_profiles_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(ConversationProfilesClient, "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(ConversationProfilesClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + ( + ConversationProfilesClient, + transports.ConversationProfilesGrpcTransport, + "grpc", + "true", + ), + ( + ConversationProfilesAsyncClient, + transports.ConversationProfilesGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + ( + ConversationProfilesClient, + transports.ConversationProfilesGrpcTransport, + "grpc", + "false", + ), + ( + ConversationProfilesAsyncClient, + transports.ConversationProfilesGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + ConversationProfilesClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationProfilesClient), +) +@mock.patch.object( + ConversationProfilesAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationProfilesAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_conversation_profiles_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + ConversationProfilesClient, + transports.ConversationProfilesGrpcTransport, + "grpc", + ), + ( + ConversationProfilesAsyncClient, + transports.ConversationProfilesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_conversation_profiles_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + ( + ConversationProfilesClient, + transports.ConversationProfilesGrpcTransport, + "grpc", + ), + ( + ConversationProfilesAsyncClient, + transports.ConversationProfilesGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_conversation_profiles_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_conversation_profiles_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflow_v2beta1.services.conversation_profiles.transports.ConversationProfilesGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = ConversationProfilesClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_list_conversation_profiles( + transport: str = "grpc", + request_type=conversation_profile.ListConversationProfilesRequest, +): + client = ConversationProfilesClient( + 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_conversation_profiles), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ListConversationProfilesResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_conversation_profiles(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.ListConversationProfilesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListConversationProfilesPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_conversation_profiles_from_dict(): + test_list_conversation_profiles(request_type=dict) + + +def test_list_conversation_profiles_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + client.list_conversation_profiles() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.ListConversationProfilesRequest() + + +@pytest.mark.asyncio +async def test_list_conversation_profiles_async( + transport: str = "grpc_asyncio", + request_type=conversation_profile.ListConversationProfilesRequest, +): + client = ConversationProfilesAsyncClient( + 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_conversation_profiles), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ListConversationProfilesResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_conversation_profiles(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.ListConversationProfilesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListConversationProfilesAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_conversation_profiles_async_from_dict(): + await test_list_conversation_profiles_async(request_type=dict) + + +def test_list_conversation_profiles_field_headers(): + client = ConversationProfilesClient(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 = conversation_profile.ListConversationProfilesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + call.return_value = conversation_profile.ListConversationProfilesResponse() + + client.list_conversation_profiles(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_conversation_profiles_field_headers_async(): + client = ConversationProfilesAsyncClient( + 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 = conversation_profile.ListConversationProfilesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ListConversationProfilesResponse() + ) + + await client.list_conversation_profiles(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_conversation_profiles_flattened(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ListConversationProfilesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_conversation_profiles(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_conversation_profiles_flattened_error(): + client = ConversationProfilesClient(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_conversation_profiles( + conversation_profile.ListConversationProfilesRequest(), + parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_conversation_profiles_flattened_async(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ListConversationProfilesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ListConversationProfilesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_conversation_profiles(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_conversation_profiles_flattened_error_async(): + client = ConversationProfilesAsyncClient( + 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_conversation_profiles( + conversation_profile.ListConversationProfilesRequest(), + parent="parent_value", + ) + + +def test_list_conversation_profiles_pager(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + next_page_token="abc", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[], next_page_token="def", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[conversation_profile.ConversationProfile(),], + next_page_token="ghi", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_conversation_profiles(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all( + isinstance(i, conversation_profile.ConversationProfile) for i in results + ) + + +def test_list_conversation_profiles_pages(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + next_page_token="abc", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[], next_page_token="def", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[conversation_profile.ConversationProfile(),], + next_page_token="ghi", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + ), + RuntimeError, + ) + pages = list(client.list_conversation_profiles(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_conversation_profiles_async_pager(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + next_page_token="abc", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[], next_page_token="def", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[conversation_profile.ConversationProfile(),], + next_page_token="ghi", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + ), + RuntimeError, + ) + async_pager = await client.list_conversation_profiles(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, conversation_profile.ConversationProfile) for i in responses + ) + + +@pytest.mark.asyncio +async def test_list_conversation_profiles_async_pages(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversation_profiles), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + next_page_token="abc", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[], next_page_token="def", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[conversation_profile.ConversationProfile(),], + next_page_token="ghi", + ), + conversation_profile.ListConversationProfilesResponse( + conversation_profiles=[ + conversation_profile.ConversationProfile(), + conversation_profile.ConversationProfile(), + ], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_conversation_profiles(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_conversation_profile( + transport: str = "grpc", + request_type=conversation_profile.GetConversationProfileRequest, +): + client = ConversationProfilesClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + + response = client.get_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.GetConversationProfileRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +def test_get_conversation_profile_from_dict(): + test_get_conversation_profile(request_type=dict) + + +def test_get_conversation_profile_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_conversation_profile), "__call__" + ) as call: + client.get_conversation_profile() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.GetConversationProfileRequest() + + +@pytest.mark.asyncio +async def test_get_conversation_profile_async( + transport: str = "grpc_asyncio", + request_type=conversation_profile.GetConversationProfileRequest, +): + client = ConversationProfilesAsyncClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + ) + + response = await client.get_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.GetConversationProfileRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +@pytest.mark.asyncio +async def test_get_conversation_profile_async_from_dict(): + await test_get_conversation_profile_async(request_type=dict) + + +def test_get_conversation_profile_field_headers(): + client = ConversationProfilesClient(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 = conversation_profile.GetConversationProfileRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_conversation_profile), "__call__" + ) as call: + call.return_value = conversation_profile.ConversationProfile() + + client.get_conversation_profile(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_conversation_profile_field_headers_async(): + client = ConversationProfilesAsyncClient( + 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 = conversation_profile.GetConversationProfileRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_conversation_profile), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ConversationProfile() + ) + + await client.get_conversation_profile(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_conversation_profile_flattened(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ConversationProfile() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_conversation_profile(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_conversation_profile_flattened_error(): + client = ConversationProfilesClient(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_conversation_profile( + conversation_profile.GetConversationProfileRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_conversation_profile_flattened_async(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation_profile.ConversationProfile() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation_profile.ConversationProfile() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_conversation_profile(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_conversation_profile_flattened_error_async(): + client = ConversationProfilesAsyncClient( + 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_conversation_profile( + conversation_profile.GetConversationProfileRequest(), name="name_value", + ) + + +def test_create_conversation_profile( + transport: str = "grpc", + request_type=gcd_conversation_profile.CreateConversationProfileRequest, +): + client = ConversationProfilesClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + + response = client.create_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.CreateConversationProfileRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +def test_create_conversation_profile_from_dict(): + test_create_conversation_profile(request_type=dict) + + +def test_create_conversation_profile_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation_profile), "__call__" + ) as call: + client.create_conversation_profile() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.CreateConversationProfileRequest() + + +@pytest.mark.asyncio +async def test_create_conversation_profile_async( + transport: str = "grpc_asyncio", + request_type=gcd_conversation_profile.CreateConversationProfileRequest, +): + client = ConversationProfilesAsyncClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + ) + + response = await client.create_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.CreateConversationProfileRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +@pytest.mark.asyncio +async def test_create_conversation_profile_async_from_dict(): + await test_create_conversation_profile_async(request_type=dict) + + +def test_create_conversation_profile_field_headers(): + client = ConversationProfilesClient(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 = gcd_conversation_profile.CreateConversationProfileRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation_profile), "__call__" + ) as call: + call.return_value = gcd_conversation_profile.ConversationProfile() + + client.create_conversation_profile(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_conversation_profile_field_headers_async(): + client = ConversationProfilesAsyncClient( + 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 = gcd_conversation_profile.CreateConversationProfileRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation_profile), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile() + ) + + await client.create_conversation_profile(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_conversation_profile_flattened(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_conversation_profile( + parent="parent_value", + conversation_profile=gcd_conversation_profile.ConversationProfile( + 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 + ].conversation_profile == gcd_conversation_profile.ConversationProfile( + name="name_value" + ) + + +def test_create_conversation_profile_flattened_error(): + client = ConversationProfilesClient(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_conversation_profile( + gcd_conversation_profile.CreateConversationProfileRequest(), + parent="parent_value", + conversation_profile=gcd_conversation_profile.ConversationProfile( + name="name_value" + ), + ) + + +@pytest.mark.asyncio +async def test_create_conversation_profile_flattened_async(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_conversation_profile( + parent="parent_value", + conversation_profile=gcd_conversation_profile.ConversationProfile( + 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 + ].conversation_profile == gcd_conversation_profile.ConversationProfile( + name="name_value" + ) + + +@pytest.mark.asyncio +async def test_create_conversation_profile_flattened_error_async(): + client = ConversationProfilesAsyncClient( + 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_conversation_profile( + gcd_conversation_profile.CreateConversationProfileRequest(), + parent="parent_value", + conversation_profile=gcd_conversation_profile.ConversationProfile( + name="name_value" + ), + ) + + +def test_update_conversation_profile( + transport: str = "grpc", + request_type=gcd_conversation_profile.UpdateConversationProfileRequest, +): + client = ConversationProfilesClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + + response = client.update_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.UpdateConversationProfileRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +def test_update_conversation_profile_from_dict(): + test_update_conversation_profile(request_type=dict) + + +def test_update_conversation_profile_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_conversation_profile), "__call__" + ) as call: + client.update_conversation_profile() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.UpdateConversationProfileRequest() + + +@pytest.mark.asyncio +async def test_update_conversation_profile_async( + transport: str = "grpc_asyncio", + request_type=gcd_conversation_profile.UpdateConversationProfileRequest, +): + client = ConversationProfilesAsyncClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile( + name="name_value", + display_name="display_name_value", + language_code="language_code_value", + ) + ) + + response = await client.update_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation_profile.UpdateConversationProfileRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_conversation_profile.ConversationProfile) + + assert response.name == "name_value" + + assert response.display_name == "display_name_value" + + assert response.language_code == "language_code_value" + + +@pytest.mark.asyncio +async def test_update_conversation_profile_async_from_dict(): + await test_update_conversation_profile_async(request_type=dict) + + +def test_update_conversation_profile_field_headers(): + client = ConversationProfilesClient(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 = gcd_conversation_profile.UpdateConversationProfileRequest() + request.conversation_profile.name = "conversation_profile.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_conversation_profile), "__call__" + ) as call: + call.return_value = gcd_conversation_profile.ConversationProfile() + + client.update_conversation_profile(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", + "conversation_profile.name=conversation_profile.name/value", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_update_conversation_profile_field_headers_async(): + client = ConversationProfilesAsyncClient( + 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 = gcd_conversation_profile.UpdateConversationProfileRequest() + request.conversation_profile.name = "conversation_profile.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_conversation_profile), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile() + ) + + await client.update_conversation_profile(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", + "conversation_profile.name=conversation_profile.name/value", + ) in kw["metadata"] + + +def test_update_conversation_profile_flattened(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_conversation_profile( + conversation_profile=gcd_conversation_profile.ConversationProfile( + 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 + ].conversation_profile == gcd_conversation_profile.ConversationProfile( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_conversation_profile_flattened_error(): + client = ConversationProfilesClient(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_conversation_profile( + gcd_conversation_profile.UpdateConversationProfileRequest(), + conversation_profile=gcd_conversation_profile.ConversationProfile( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_conversation_profile_flattened_async(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation_profile.ConversationProfile() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation_profile.ConversationProfile() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_conversation_profile( + conversation_profile=gcd_conversation_profile.ConversationProfile( + 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 + ].conversation_profile == gcd_conversation_profile.ConversationProfile( + name="name_value" + ) + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_conversation_profile_flattened_error_async(): + client = ConversationProfilesAsyncClient( + 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_conversation_profile( + gcd_conversation_profile.UpdateConversationProfileRequest(), + conversation_profile=gcd_conversation_profile.ConversationProfile( + name="name_value" + ), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_delete_conversation_profile( + transport: str = "grpc", + request_type=conversation_profile.DeleteConversationProfileRequest, +): + client = ConversationProfilesClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.DeleteConversationProfileRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_conversation_profile_from_dict(): + test_delete_conversation_profile(request_type=dict) + + +def test_delete_conversation_profile_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_conversation_profile), "__call__" + ) as call: + client.delete_conversation_profile() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.DeleteConversationProfileRequest() + + +@pytest.mark.asyncio +async def test_delete_conversation_profile_async( + transport: str = "grpc_asyncio", + request_type=conversation_profile.DeleteConversationProfileRequest, +): + client = ConversationProfilesAsyncClient( + 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_conversation_profile), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_conversation_profile(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation_profile.DeleteConversationProfileRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_conversation_profile_async_from_dict(): + await test_delete_conversation_profile_async(request_type=dict) + + +def test_delete_conversation_profile_field_headers(): + client = ConversationProfilesClient(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 = conversation_profile.DeleteConversationProfileRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_conversation_profile), "__call__" + ) as call: + call.return_value = None + + client.delete_conversation_profile(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_conversation_profile_field_headers_async(): + client = ConversationProfilesAsyncClient( + 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 = conversation_profile.DeleteConversationProfileRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_conversation_profile), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_conversation_profile(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_conversation_profile_flattened(): + client = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_conversation_profile), "__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_conversation_profile(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_conversation_profile_flattened_error(): + client = ConversationProfilesClient(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_conversation_profile( + conversation_profile.DeleteConversationProfileRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_conversation_profile_flattened_async(): + client = ConversationProfilesAsyncClient( + credentials=credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_conversation_profile), "__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_conversation_profile(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_conversation_profile_flattened_error_async(): + client = ConversationProfilesAsyncClient( + 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_conversation_profile( + conversation_profile.DeleteConversationProfileRequest(), name="name_value", + ) + + +def test_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.ConversationProfilesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.ConversationProfilesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationProfilesClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.ConversationProfilesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationProfilesClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.ConversationProfilesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = ConversationProfilesClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.ConversationProfilesGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.ConversationProfilesGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationProfilesGrpcTransport, + transports.ConversationProfilesGrpcAsyncIOTransport, + ], +) +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 = ConversationProfilesClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.ConversationProfilesGrpcTransport,) + + +def test_conversation_profiles_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.ConversationProfilesTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_conversation_profiles_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflow_v2beta1.services.conversation_profiles.transports.ConversationProfilesTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.ConversationProfilesTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "list_conversation_profiles", + "get_conversation_profile", + "create_conversation_profile", + "update_conversation_profile", + "delete_conversation_profile", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_conversation_profiles_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.dialogflow_v2beta1.services.conversation_profiles.transports.ConversationProfilesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ConversationProfilesTransport( + 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_conversation_profiles_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.dialogflow_v2beta1.services.conversation_profiles.transports.ConversationProfilesTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ConversationProfilesTransport() + adc.assert_called_once() + + +def test_conversation_profiles_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) + ConversationProfilesClient() + 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_conversation_profiles_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.ConversationProfilesGrpcTransport( + 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", + ) + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationProfilesGrpcTransport, + transports.ConversationProfilesGrpcAsyncIOTransport, + ], +) +def test_conversation_profiles_grpc_transport_client_cert_source_for_mtls( + transport_class, +): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_conversation_profiles_host_no_port(): + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_conversation_profiles_host_with_port(): + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_conversation_profiles_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ConversationProfilesGrpcTransport( + 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_conversation_profiles_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ConversationProfilesGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationProfilesGrpcTransport, + transports.ConversationProfilesGrpcAsyncIOTransport, + ], +) +def test_conversation_profiles_transport_channel_mtls_with_client_cert_source( + transport_class, +): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationProfilesGrpcTransport, + transports.ConversationProfilesGrpcAsyncIOTransport, + ], +) +def test_conversation_profiles_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_agent_path(): + project = "squid" + + expected = "projects/{project}/agent".format(project=project,) + actual = ConversationProfilesClient.agent_path(project) + assert expected == actual + + +def test_parse_agent_path(): + expected = { + "project": "clam", + } + path = ConversationProfilesClient.agent_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_agent_path(path) + assert expected == actual + + +def test_conversation_profile_path(): + project = "whelk" + conversation_profile = "octopus" + + expected = "projects/{project}/conversationProfiles/{conversation_profile}".format( + project=project, conversation_profile=conversation_profile, + ) + actual = ConversationProfilesClient.conversation_profile_path( + project, conversation_profile + ) + assert expected == actual + + +def test_parse_conversation_profile_path(): + expected = { + "project": "oyster", + "conversation_profile": "nudibranch", + } + path = ConversationProfilesClient.conversation_profile_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_conversation_profile_path(path) + assert expected == actual + + +def test_document_path(): + project = "cuttlefish" + knowledge_base = "mussel" + document = "winkle" + + expected = "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, knowledge_base=knowledge_base, document=document, + ) + actual = ConversationProfilesClient.document_path(project, knowledge_base, document) + assert expected == actual + + +def test_parse_document_path(): + expected = { + "project": "nautilus", + "knowledge_base": "scallop", + "document": "abalone", + } + path = ConversationProfilesClient.document_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_document_path(path) + assert expected == actual + + +def test_knowledge_base_path(): + project = "squid" + knowledge_base = "clam" + + expected = "projects/{project}/knowledgeBases/{knowledge_base}".format( + project=project, knowledge_base=knowledge_base, + ) + actual = ConversationProfilesClient.knowledge_base_path(project, knowledge_base) + assert expected == actual + + +def test_parse_knowledge_base_path(): + expected = { + "project": "whelk", + "knowledge_base": "octopus", + } + path = ConversationProfilesClient.knowledge_base_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_knowledge_base_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "oyster" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = ConversationProfilesClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nudibranch", + } + path = ConversationProfilesClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "cuttlefish" + + expected = "folders/{folder}".format(folder=folder,) + actual = ConversationProfilesClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "mussel", + } + path = ConversationProfilesClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "winkle" + + expected = "organizations/{organization}".format(organization=organization,) + actual = ConversationProfilesClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "nautilus", + } + path = ConversationProfilesClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "scallop" + + expected = "projects/{project}".format(project=project,) + actual = ConversationProfilesClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "abalone", + } + path = ConversationProfilesClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.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 = ConversationProfilesClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "whelk", + "location": "octopus", + } + path = ConversationProfilesClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationProfilesClient.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.ConversationProfilesTransport, "_prep_wrapped_messages" + ) as prep: + client = ConversationProfilesClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.ConversationProfilesTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = ConversationProfilesClient.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/dialogflow_v2beta1/test_conversations.py b/tests/unit/gapic/dialogflow_v2beta1/test_conversations.py new file mode 100644 index 000000000..96d430f8a --- /dev/null +++ b/tests/unit/gapic/dialogflow_v2beta1/test_conversations.py @@ -0,0 +1,3606 @@ +# -*- 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.dialogflow_v2beta1.services.conversations import ( + ConversationsAsyncClient, +) +from google.cloud.dialogflow_v2beta1.services.conversations import ConversationsClient +from google.cloud.dialogflow_v2beta1.services.conversations import pagers +from google.cloud.dialogflow_v2beta1.services.conversations import transports +from google.cloud.dialogflow_v2beta1.types import conversation +from google.cloud.dialogflow_v2beta1.types import conversation as gcd_conversation +from google.cloud.dialogflow_v2beta1.types import participant +from google.cloud.dialogflow_v2beta1.types import session +from google.oauth2 import service_account +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 ConversationsClient._get_default_mtls_endpoint(None) is None + assert ( + ConversationsClient._get_default_mtls_endpoint(api_endpoint) + == api_mtls_endpoint + ) + assert ( + ConversationsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + ConversationsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ConversationsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ConversationsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + ) + + +@pytest.mark.parametrize( + "client_class", [ConversationsClient, ConversationsAsyncClient,] +) +def test_conversations_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize( + "client_class", [ConversationsClient, ConversationsAsyncClient,] +) +def test_conversations_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 + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_conversations_client_get_transport_class(): + transport = ConversationsClient.get_transport_class() + available_transports = [ + transports.ConversationsGrpcTransport, + ] + assert transport in available_transports + + transport = ConversationsClient.get_transport_class("grpc") + assert transport == transports.ConversationsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ConversationsClient, transports.ConversationsGrpcTransport, "grpc"), + ( + ConversationsAsyncClient, + transports.ConversationsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + ConversationsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationsClient), +) +@mock.patch.object( + ConversationsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationsAsyncClient), +) +def test_conversations_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(ConversationsClient, "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(ConversationsClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (ConversationsClient, transports.ConversationsGrpcTransport, "grpc", "true"), + ( + ConversationsAsyncClient, + transports.ConversationsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (ConversationsClient, transports.ConversationsGrpcTransport, "grpc", "false"), + ( + ConversationsAsyncClient, + transports.ConversationsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + ConversationsClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationsClient), +) +@mock.patch.object( + ConversationsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ConversationsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_conversations_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ConversationsClient, transports.ConversationsGrpcTransport, "grpc"), + ( + ConversationsAsyncClient, + transports.ConversationsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_conversations_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ConversationsClient, transports.ConversationsGrpcTransport, "grpc"), + ( + ConversationsAsyncClient, + transports.ConversationsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_conversations_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_conversations_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflow_v2beta1.services.conversations.transports.ConversationsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = ConversationsClient( + client_options={"api_endpoint": "squid.clam.whelk"} + ) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_create_conversation( + transport: str = "grpc", request_type=gcd_conversation.CreateConversationRequest +): + client = ConversationsClient( + 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_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation.Conversation( + name="name_value", + lifecycle_state=gcd_conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=gcd_conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + + response = client.create_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation.CreateConversationRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state + == gcd_conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == gcd_conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +def test_create_conversation_from_dict(): + test_create_conversation(request_type=dict) + + +def test_create_conversation_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation), "__call__" + ) as call: + client.create_conversation() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation.CreateConversationRequest() + + +@pytest.mark.asyncio +async def test_create_conversation_async( + transport: str = "grpc_asyncio", + request_type=gcd_conversation.CreateConversationRequest, +): + client = ConversationsAsyncClient( + 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_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation.Conversation( + name="name_value", + lifecycle_state=gcd_conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=gcd_conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + ) + + response = await client.create_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_conversation.CreateConversationRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state + == gcd_conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == gcd_conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +@pytest.mark.asyncio +async def test_create_conversation_async_from_dict(): + await test_create_conversation_async(request_type=dict) + + +def test_create_conversation_field_headers(): + client = ConversationsClient(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 = gcd_conversation.CreateConversationRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation), "__call__" + ) as call: + call.return_value = gcd_conversation.Conversation() + + client.create_conversation(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_conversation_field_headers_async(): + client = ConversationsAsyncClient(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 = gcd_conversation.CreateConversationRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation.Conversation() + ) + + await client.create_conversation(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_conversation_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation.Conversation() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_conversation( + parent="parent_value", + conversation=gcd_conversation.Conversation(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].conversation == gcd_conversation.Conversation(name="name_value") + + +def test_create_conversation_flattened_error(): + client = ConversationsClient(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_conversation( + gcd_conversation.CreateConversationRequest(), + parent="parent_value", + conversation=gcd_conversation.Conversation(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_conversation_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_conversation.Conversation() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_conversation.Conversation() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_conversation( + parent="parent_value", + conversation=gcd_conversation.Conversation(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].conversation == gcd_conversation.Conversation(name="name_value") + + +@pytest.mark.asyncio +async def test_create_conversation_flattened_error_async(): + client = ConversationsAsyncClient(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_conversation( + gcd_conversation.CreateConversationRequest(), + parent="parent_value", + conversation=gcd_conversation.Conversation(name="name_value"), + ) + + +def test_list_conversations( + transport: str = "grpc", request_type=conversation.ListConversationsRequest +): + client = ConversationsClient( + 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_conversations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListConversationsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_conversations(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListConversationsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListConversationsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_conversations_from_dict(): + test_list_conversations(request_type=dict) + + +def test_list_conversations_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + client.list_conversations() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListConversationsRequest() + + +@pytest.mark.asyncio +async def test_list_conversations_async( + transport: str = "grpc_asyncio", request_type=conversation.ListConversationsRequest +): + client = ConversationsAsyncClient( + 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_conversations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListConversationsResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_conversations(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListConversationsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListConversationsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_conversations_async_from_dict(): + await test_list_conversations_async(request_type=dict) + + +def test_list_conversations_field_headers(): + client = ConversationsClient(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 = conversation.ListConversationsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + call.return_value = conversation.ListConversationsResponse() + + client.list_conversations(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_conversations_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.ListConversationsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListConversationsResponse() + ) + + await client.list_conversations(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_conversations_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListConversationsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_conversations(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_conversations_flattened_error(): + client = ConversationsClient(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_conversations( + conversation.ListConversationsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_conversations_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListConversationsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListConversationsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_conversations(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_conversations_flattened_error_async(): + client = ConversationsAsyncClient(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_conversations( + conversation.ListConversationsRequest(), parent="parent_value", + ) + + +def test_list_conversations_pager(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + conversation.Conversation(), + ], + next_page_token="abc", + ), + conversation.ListConversationsResponse( + conversations=[], next_page_token="def", + ), + conversation.ListConversationsResponse( + conversations=[conversation.Conversation(),], next_page_token="ghi", + ), + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + ], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_conversations(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, conversation.Conversation) for i in results) + + +def test_list_conversations_pages(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + conversation.Conversation(), + ], + next_page_token="abc", + ), + conversation.ListConversationsResponse( + conversations=[], next_page_token="def", + ), + conversation.ListConversationsResponse( + conversations=[conversation.Conversation(),], next_page_token="ghi", + ), + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + ], + ), + RuntimeError, + ) + pages = list(client.list_conversations(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_conversations_async_pager(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + conversation.Conversation(), + ], + next_page_token="abc", + ), + conversation.ListConversationsResponse( + conversations=[], next_page_token="def", + ), + conversation.ListConversationsResponse( + conversations=[conversation.Conversation(),], next_page_token="ghi", + ), + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + ], + ), + RuntimeError, + ) + async_pager = await client.list_conversations(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, conversation.Conversation) for i in responses) + + +@pytest.mark.asyncio +async def test_list_conversations_async_pages(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_conversations), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + conversation.Conversation(), + ], + next_page_token="abc", + ), + conversation.ListConversationsResponse( + conversations=[], next_page_token="def", + ), + conversation.ListConversationsResponse( + conversations=[conversation.Conversation(),], next_page_token="ghi", + ), + conversation.ListConversationsResponse( + conversations=[ + conversation.Conversation(), + conversation.Conversation(), + ], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_conversations(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_conversation( + transport: str = "grpc", request_type=conversation.GetConversationRequest +): + client = ConversationsClient( + 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_conversation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation( + name="name_value", + lifecycle_state=conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + + response = client.get_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.GetConversationRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state == conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +def test_get_conversation_from_dict(): + test_get_conversation(request_type=dict) + + +def test_get_conversation_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_conversation), "__call__") as call: + client.get_conversation() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.GetConversationRequest() + + +@pytest.mark.asyncio +async def test_get_conversation_async( + transport: str = "grpc_asyncio", request_type=conversation.GetConversationRequest +): + client = ConversationsAsyncClient( + 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_conversation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation( + name="name_value", + lifecycle_state=conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + ) + + response = await client.get_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.GetConversationRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state == conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +@pytest.mark.asyncio +async def test_get_conversation_async_from_dict(): + await test_get_conversation_async(request_type=dict) + + +def test_get_conversation_field_headers(): + client = ConversationsClient(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 = conversation.GetConversationRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_conversation), "__call__") as call: + call.return_value = conversation.Conversation() + + client.get_conversation(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_conversation_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.GetConversationRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_conversation), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation() + ) + + await client.get_conversation(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_conversation_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_conversation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_conversation(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_conversation_flattened_error(): + client = ConversationsClient(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_conversation( + conversation.GetConversationRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_conversation_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_conversation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_conversation(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_conversation_flattened_error_async(): + client = ConversationsAsyncClient(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_conversation( + conversation.GetConversationRequest(), name="name_value", + ) + + +def test_complete_conversation( + transport: str = "grpc", request_type=conversation.CompleteConversationRequest +): + client = ConversationsClient( + 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.complete_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation( + name="name_value", + lifecycle_state=conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + + response = client.complete_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CompleteConversationRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state == conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +def test_complete_conversation_from_dict(): + test_complete_conversation(request_type=dict) + + +def test_complete_conversation_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.complete_conversation), "__call__" + ) as call: + client.complete_conversation() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CompleteConversationRequest() + + +@pytest.mark.asyncio +async def test_complete_conversation_async( + transport: str = "grpc_asyncio", + request_type=conversation.CompleteConversationRequest, +): + client = ConversationsAsyncClient( + 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.complete_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation( + name="name_value", + lifecycle_state=conversation.Conversation.LifecycleState.IN_PROGRESS, + conversation_profile="conversation_profile_value", + conversation_stage=conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE, + ) + ) + + response = await client.complete_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CompleteConversationRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.Conversation) + + assert response.name == "name_value" + + assert ( + response.lifecycle_state == conversation.Conversation.LifecycleState.IN_PROGRESS + ) + + assert response.conversation_profile == "conversation_profile_value" + + assert ( + response.conversation_stage + == conversation.Conversation.ConversationStage.VIRTUAL_AGENT_STAGE + ) + + +@pytest.mark.asyncio +async def test_complete_conversation_async_from_dict(): + await test_complete_conversation_async(request_type=dict) + + +def test_complete_conversation_field_headers(): + client = ConversationsClient(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 = conversation.CompleteConversationRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.complete_conversation), "__call__" + ) as call: + call.return_value = conversation.Conversation() + + client.complete_conversation(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_complete_conversation_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.CompleteConversationRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.complete_conversation), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation() + ) + + await client.complete_conversation(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_complete_conversation_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.complete_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.complete_conversation(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_complete_conversation_flattened_error(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.complete_conversation( + conversation.CompleteConversationRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_complete_conversation_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.complete_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.Conversation() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.Conversation() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.complete_conversation(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_complete_conversation_flattened_error_async(): + client = ConversationsAsyncClient(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.complete_conversation( + conversation.CompleteConversationRequest(), name="name_value", + ) + + +def test_create_call_matcher( + transport: str = "grpc", request_type=conversation.CreateCallMatcherRequest +): + client = ConversationsClient( + 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_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.CallMatcher( + name="name_value", + to_header="to_header_value", + from_header="from_header_value", + call_id_header="call_id_header_value", + ) + + response = client.create_call_matcher(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CreateCallMatcherRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, conversation.CallMatcher) + + assert response.name == "name_value" + + assert response.to_header == "to_header_value" + + assert response.from_header == "from_header_value" + + assert response.call_id_header == "call_id_header_value" + + +def test_create_call_matcher_from_dict(): + test_create_call_matcher(request_type=dict) + + +def test_create_call_matcher_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_call_matcher), "__call__" + ) as call: + client.create_call_matcher() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CreateCallMatcherRequest() + + +@pytest.mark.asyncio +async def test_create_call_matcher_async( + transport: str = "grpc_asyncio", request_type=conversation.CreateCallMatcherRequest +): + client = ConversationsAsyncClient( + 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_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.CallMatcher( + name="name_value", + to_header="to_header_value", + from_header="from_header_value", + call_id_header="call_id_header_value", + ) + ) + + response = await client.create_call_matcher(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.CreateCallMatcherRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.CallMatcher) + + assert response.name == "name_value" + + assert response.to_header == "to_header_value" + + assert response.from_header == "from_header_value" + + assert response.call_id_header == "call_id_header_value" + + +@pytest.mark.asyncio +async def test_create_call_matcher_async_from_dict(): + await test_create_call_matcher_async(request_type=dict) + + +def test_create_call_matcher_field_headers(): + client = ConversationsClient(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 = conversation.CreateCallMatcherRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_call_matcher), "__call__" + ) as call: + call.return_value = conversation.CallMatcher() + + client.create_call_matcher(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_call_matcher_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.CreateCallMatcherRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_call_matcher), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.CallMatcher() + ) + + await client.create_call_matcher(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_call_matcher_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.CallMatcher() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_call_matcher( + parent="parent_value", + call_matcher=conversation.CallMatcher(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].call_matcher == conversation.CallMatcher(name="name_value") + + +def test_create_call_matcher_flattened_error(): + client = ConversationsClient(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_call_matcher( + conversation.CreateCallMatcherRequest(), + parent="parent_value", + call_matcher=conversation.CallMatcher(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_call_matcher_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.CallMatcher() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.CallMatcher() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_call_matcher( + parent="parent_value", + call_matcher=conversation.CallMatcher(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].call_matcher == conversation.CallMatcher(name="name_value") + + +@pytest.mark.asyncio +async def test_create_call_matcher_flattened_error_async(): + client = ConversationsAsyncClient(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_call_matcher( + conversation.CreateCallMatcherRequest(), + parent="parent_value", + call_matcher=conversation.CallMatcher(name="name_value"), + ) + + +def test_list_call_matchers( + transport: str = "grpc", request_type=conversation.ListCallMatchersRequest +): + client = ConversationsClient( + 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_call_matchers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListCallMatchersResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_call_matchers(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListCallMatchersRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListCallMatchersPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_call_matchers_from_dict(): + test_list_call_matchers(request_type=dict) + + +def test_list_call_matchers_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + client.list_call_matchers() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListCallMatchersRequest() + + +@pytest.mark.asyncio +async def test_list_call_matchers_async( + transport: str = "grpc_asyncio", request_type=conversation.ListCallMatchersRequest +): + client = ConversationsAsyncClient( + 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_call_matchers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListCallMatchersResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_call_matchers(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListCallMatchersRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListCallMatchersAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_call_matchers_async_from_dict(): + await test_list_call_matchers_async(request_type=dict) + + +def test_list_call_matchers_field_headers(): + client = ConversationsClient(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 = conversation.ListCallMatchersRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + call.return_value = conversation.ListCallMatchersResponse() + + client.list_call_matchers(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_call_matchers_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.ListCallMatchersRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListCallMatchersResponse() + ) + + await client.list_call_matchers(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_call_matchers_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListCallMatchersResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_call_matchers(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_call_matchers_flattened_error(): + client = ConversationsClient(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_call_matchers( + conversation.ListCallMatchersRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_call_matchers_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListCallMatchersResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListCallMatchersResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_call_matchers(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_call_matchers_flattened_error_async(): + client = ConversationsAsyncClient(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_call_matchers( + conversation.ListCallMatchersRequest(), parent="parent_value", + ) + + +def test_list_call_matchers_pager(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListCallMatchersResponse( + call_matchers=[ + conversation.CallMatcher(), + conversation.CallMatcher(), + conversation.CallMatcher(), + ], + next_page_token="abc", + ), + conversation.ListCallMatchersResponse( + call_matchers=[], next_page_token="def", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(),], next_page_token="ghi", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(), conversation.CallMatcher(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_call_matchers(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, conversation.CallMatcher) for i in results) + + +def test_list_call_matchers_pages(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListCallMatchersResponse( + call_matchers=[ + conversation.CallMatcher(), + conversation.CallMatcher(), + conversation.CallMatcher(), + ], + next_page_token="abc", + ), + conversation.ListCallMatchersResponse( + call_matchers=[], next_page_token="def", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(),], next_page_token="ghi", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(), conversation.CallMatcher(),], + ), + RuntimeError, + ) + pages = list(client.list_call_matchers(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_call_matchers_async_pager(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListCallMatchersResponse( + call_matchers=[ + conversation.CallMatcher(), + conversation.CallMatcher(), + conversation.CallMatcher(), + ], + next_page_token="abc", + ), + conversation.ListCallMatchersResponse( + call_matchers=[], next_page_token="def", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(),], next_page_token="ghi", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(), conversation.CallMatcher(),], + ), + RuntimeError, + ) + async_pager = await client.list_call_matchers(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, conversation.CallMatcher) for i in responses) + + +@pytest.mark.asyncio +async def test_list_call_matchers_async_pages(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_call_matchers), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListCallMatchersResponse( + call_matchers=[ + conversation.CallMatcher(), + conversation.CallMatcher(), + conversation.CallMatcher(), + ], + next_page_token="abc", + ), + conversation.ListCallMatchersResponse( + call_matchers=[], next_page_token="def", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(),], next_page_token="ghi", + ), + conversation.ListCallMatchersResponse( + call_matchers=[conversation.CallMatcher(), conversation.CallMatcher(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_call_matchers(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_call_matcher( + transport: str = "grpc", request_type=conversation.DeleteCallMatcherRequest +): + client = ConversationsClient( + 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_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_call_matcher(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.DeleteCallMatcherRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_call_matcher_from_dict(): + test_delete_call_matcher(request_type=dict) + + +def test_delete_call_matcher_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_call_matcher), "__call__" + ) as call: + client.delete_call_matcher() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.DeleteCallMatcherRequest() + + +@pytest.mark.asyncio +async def test_delete_call_matcher_async( + transport: str = "grpc_asyncio", request_type=conversation.DeleteCallMatcherRequest +): + client = ConversationsAsyncClient( + 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_call_matcher), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + response = await client.delete_call_matcher(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.DeleteCallMatcherRequest() + + # Establish that the response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_call_matcher_async_from_dict(): + await test_delete_call_matcher_async(request_type=dict) + + +def test_delete_call_matcher_field_headers(): + client = ConversationsClient(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 = conversation.DeleteCallMatcherRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_call_matcher), "__call__" + ) as call: + call.return_value = None + + client.delete_call_matcher(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_call_matcher_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.DeleteCallMatcherRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_call_matcher), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + + await client.delete_call_matcher(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_call_matcher_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_call_matcher), "__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_call_matcher(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_call_matcher_flattened_error(): + client = ConversationsClient(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_call_matcher( + conversation.DeleteCallMatcherRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_delete_call_matcher_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_call_matcher), "__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_call_matcher(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_call_matcher_flattened_error_async(): + client = ConversationsAsyncClient(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_call_matcher( + conversation.DeleteCallMatcherRequest(), name="name_value", + ) + + +def test_batch_create_messages( + transport: str = "grpc", request_type=conversation.BatchCreateMessagesRequest +): + client = ConversationsClient( + 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.batch_create_messages), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.BatchCreateMessagesResponse() + + response = client.batch_create_messages(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.BatchCreateMessagesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, conversation.BatchCreateMessagesResponse) + + +def test_batch_create_messages_from_dict(): + test_batch_create_messages(request_type=dict) + + +def test_batch_create_messages_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_create_messages), "__call__" + ) as call: + client.batch_create_messages() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.BatchCreateMessagesRequest() + + +@pytest.mark.asyncio +async def test_batch_create_messages_async( + transport: str = "grpc_asyncio", + request_type=conversation.BatchCreateMessagesRequest, +): + client = ConversationsAsyncClient( + 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.batch_create_messages), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.BatchCreateMessagesResponse() + ) + + response = await client.batch_create_messages(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.BatchCreateMessagesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, conversation.BatchCreateMessagesResponse) + + +@pytest.mark.asyncio +async def test_batch_create_messages_async_from_dict(): + await test_batch_create_messages_async(request_type=dict) + + +def test_batch_create_messages_field_headers(): + client = ConversationsClient(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 = conversation.BatchCreateMessagesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_create_messages), "__call__" + ) as call: + call.return_value = conversation.BatchCreateMessagesResponse() + + client.batch_create_messages(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_batch_create_messages_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.BatchCreateMessagesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_create_messages), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.BatchCreateMessagesResponse() + ) + + await client.batch_create_messages(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_batch_create_messages_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_create_messages), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.BatchCreateMessagesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.batch_create_messages(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_batch_create_messages_flattened_error(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.batch_create_messages( + conversation.BatchCreateMessagesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_batch_create_messages_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_create_messages), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.BatchCreateMessagesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.BatchCreateMessagesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.batch_create_messages(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_batch_create_messages_flattened_error_async(): + client = ConversationsAsyncClient(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.batch_create_messages( + conversation.BatchCreateMessagesRequest(), parent="parent_value", + ) + + +def test_list_messages( + transport: str = "grpc", request_type=conversation.ListMessagesRequest +): + client = ConversationsClient( + 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_messages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListMessagesResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_messages(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListMessagesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListMessagesPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_messages_from_dict(): + test_list_messages(request_type=dict) + + +def test_list_messages_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + client.list_messages() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListMessagesRequest() + + +@pytest.mark.asyncio +async def test_list_messages_async( + transport: str = "grpc_asyncio", request_type=conversation.ListMessagesRequest +): + client = ConversationsAsyncClient( + 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_messages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListMessagesResponse(next_page_token="next_page_token_value",) + ) + + response = await client.list_messages(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == conversation.ListMessagesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListMessagesAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_messages_async_from_dict(): + await test_list_messages_async(request_type=dict) + + +def test_list_messages_field_headers(): + client = ConversationsClient(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 = conversation.ListMessagesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + call.return_value = conversation.ListMessagesResponse() + + client.list_messages(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_messages_field_headers_async(): + client = ConversationsAsyncClient(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 = conversation.ListMessagesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListMessagesResponse() + ) + + await client.list_messages(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_messages_flattened(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListMessagesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_messages(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_messages_flattened_error(): + client = ConversationsClient(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_messages( + conversation.ListMessagesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_messages_flattened_async(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = conversation.ListMessagesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + conversation.ListMessagesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_messages(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_messages_flattened_error_async(): + client = ConversationsAsyncClient(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_messages( + conversation.ListMessagesRequest(), parent="parent_value", + ) + + +def test_list_messages_pager(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListMessagesResponse( + messages=[ + participant.Message(), + participant.Message(), + participant.Message(), + ], + next_page_token="abc", + ), + conversation.ListMessagesResponse(messages=[], next_page_token="def",), + conversation.ListMessagesResponse( + messages=[participant.Message(),], next_page_token="ghi", + ), + conversation.ListMessagesResponse( + messages=[participant.Message(), participant.Message(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_messages(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, participant.Message) for i in results) + + +def test_list_messages_pages(): + client = ConversationsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_messages), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListMessagesResponse( + messages=[ + participant.Message(), + participant.Message(), + participant.Message(), + ], + next_page_token="abc", + ), + conversation.ListMessagesResponse(messages=[], next_page_token="def",), + conversation.ListMessagesResponse( + messages=[participant.Message(),], next_page_token="ghi", + ), + conversation.ListMessagesResponse( + messages=[participant.Message(), participant.Message(),], + ), + RuntimeError, + ) + pages = list(client.list_messages(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_messages_async_pager(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_messages), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListMessagesResponse( + messages=[ + participant.Message(), + participant.Message(), + participant.Message(), + ], + next_page_token="abc", + ), + conversation.ListMessagesResponse(messages=[], next_page_token="def",), + conversation.ListMessagesResponse( + messages=[participant.Message(),], next_page_token="ghi", + ), + conversation.ListMessagesResponse( + messages=[participant.Message(), participant.Message(),], + ), + RuntimeError, + ) + async_pager = await client.list_messages(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, participant.Message) for i in responses) + + +@pytest.mark.asyncio +async def test_list_messages_async_pages(): + client = ConversationsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_messages), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + conversation.ListMessagesResponse( + messages=[ + participant.Message(), + participant.Message(), + participant.Message(), + ], + next_page_token="abc", + ), + conversation.ListMessagesResponse(messages=[], next_page_token="def",), + conversation.ListMessagesResponse( + messages=[participant.Message(),], next_page_token="ghi", + ), + conversation.ListMessagesResponse( + messages=[participant.Message(), participant.Message(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_messages(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.ConversationsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.ConversationsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.ConversationsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ConversationsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.ConversationsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = ConversationsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.ConversationsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.ConversationsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationsGrpcTransport, + transports.ConversationsGrpcAsyncIOTransport, + ], +) +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 = ConversationsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.ConversationsGrpcTransport,) + + +def test_conversations_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.ConversationsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_conversations_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflow_v2beta1.services.conversations.transports.ConversationsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.ConversationsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "create_conversation", + "list_conversations", + "get_conversation", + "complete_conversation", + "create_call_matcher", + "list_call_matchers", + "delete_call_matcher", + "batch_create_messages", + "list_messages", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_conversations_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.dialogflow_v2beta1.services.conversations.transports.ConversationsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ConversationsTransport( + 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_conversations_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.dialogflow_v2beta1.services.conversations.transports.ConversationsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ConversationsTransport() + adc.assert_called_once() + + +def test_conversations_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) + ConversationsClient() + 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_conversations_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.ConversationsGrpcTransport( + 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", + ) + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationsGrpcTransport, + transports.ConversationsGrpcAsyncIOTransport, + ], +) +def test_conversations_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_conversations_host_no_port(): + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_conversations_host_with_port(): + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_conversations_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ConversationsGrpcTransport( + 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_conversations_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ConversationsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationsGrpcTransport, + transports.ConversationsGrpcAsyncIOTransport, + ], +) +def test_conversations_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [ + transports.ConversationsGrpcTransport, + transports.ConversationsGrpcAsyncIOTransport, + ], +) +def test_conversations_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_call_matcher_path(): + project = "squid" + conversation = "clam" + call_matcher = "whelk" + + expected = "projects/{project}/conversations/{conversation}/callMatchers/{call_matcher}".format( + project=project, conversation=conversation, call_matcher=call_matcher, + ) + actual = ConversationsClient.call_matcher_path(project, conversation, call_matcher) + assert expected == actual + + +def test_parse_call_matcher_path(): + expected = { + "project": "octopus", + "conversation": "oyster", + "call_matcher": "nudibranch", + } + path = ConversationsClient.call_matcher_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_call_matcher_path(path) + assert expected == actual + + +def test_conversation_path(): + project = "cuttlefish" + conversation = "mussel" + + expected = "projects/{project}/conversations/{conversation}".format( + project=project, conversation=conversation, + ) + actual = ConversationsClient.conversation_path(project, conversation) + assert expected == actual + + +def test_parse_conversation_path(): + expected = { + "project": "winkle", + "conversation": "nautilus", + } + path = ConversationsClient.conversation_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_conversation_path(path) + assert expected == actual + + +def test_conversation_profile_path(): + project = "scallop" + conversation_profile = "abalone" + + expected = "projects/{project}/conversationProfiles/{conversation_profile}".format( + project=project, conversation_profile=conversation_profile, + ) + actual = ConversationsClient.conversation_profile_path( + project, conversation_profile + ) + assert expected == actual + + +def test_parse_conversation_profile_path(): + expected = { + "project": "squid", + "conversation_profile": "clam", + } + path = ConversationsClient.conversation_profile_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_conversation_profile_path(path) + assert expected == actual + + +def test_message_path(): + project = "whelk" + conversation = "octopus" + message = "oyster" + + expected = "projects/{project}/conversations/{conversation}/messages/{message}".format( + project=project, conversation=conversation, message=message, + ) + actual = ConversationsClient.message_path(project, conversation, message) + assert expected == actual + + +def test_parse_message_path(): + expected = { + "project": "nudibranch", + "conversation": "cuttlefish", + "message": "mussel", + } + path = ConversationsClient.message_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_message_path(path) + assert expected == actual + + +def test_common_billing_account_path(): + billing_account = "winkle" + + expected = "billingAccounts/{billing_account}".format( + billing_account=billing_account, + ) + actual = ConversationsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "nautilus", + } + path = ConversationsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "scallop" + + expected = "folders/{folder}".format(folder=folder,) + actual = ConversationsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "abalone", + } + path = ConversationsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "squid" + + expected = "organizations/{organization}".format(organization=organization,) + actual = ConversationsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "clam", + } + path = ConversationsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "whelk" + + expected = "projects/{project}".format(project=project,) + actual = ConversationsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "octopus", + } + path = ConversationsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.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 = ConversationsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "cuttlefish", + "location": "mussel", + } + path = ConversationsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = ConversationsClient.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.ConversationsTransport, "_prep_wrapped_messages" + ) as prep: + client = ConversationsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.ConversationsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = ConversationsClient.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/dialogflow_v2beta1/test_documents.py b/tests/unit/gapic/dialogflow_v2beta1/test_documents.py index b8cf3ba5d..0ecfd8a80 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/test_documents.py +++ b/tests/unit/gapic/dialogflow_v2beta1/test_documents.py @@ -89,7 +89,22 @@ def test__get_default_mtls_endpoint(): assert DocumentsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [DocumentsClient, DocumentsAsyncClient]) +@pytest.mark.parametrize("client_class", [DocumentsClient, DocumentsAsyncClient,]) +def test_documents_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [DocumentsClient, DocumentsAsyncClient,]) def test_documents_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -98,16 +113,21 @@ def test_documents_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_documents_client_get_transport_class(): transport = DocumentsClient.get_transport_class() - assert transport == transports.DocumentsGrpcTransport + available_transports = [ + transports.DocumentsGrpcTransport, + ] + assert transport in available_transports transport = DocumentsClient.get_transport_class("grpc") assert transport == transports.DocumentsGrpcTransport @@ -154,7 +174,7 @@ def test_documents_client_client_options(client_class, transport_class, transpor credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -170,7 +190,7 @@ def test_documents_client_client_options(client_class, transport_class, transpor credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -186,7 +206,7 @@ def test_documents_client_client_options(client_class, transport_class, transpor credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -214,7 +234,7 @@ def test_documents_client_client_options(client_class, transport_class, transpor credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -263,29 +283,25 @@ def test_documents_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -294,66 +310,53 @@ def test_documents_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -379,7 +382,7 @@ def test_documents_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -409,7 +412,7 @@ def test_documents_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -426,7 +429,7 @@ def test_documents_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -469,6 +472,22 @@ def test_list_documents_from_dict(): test_list_documents(request_type=dict) +def test_list_documents_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_documents), "__call__") as call: + client.list_documents() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == document.ListDocumentsRequest() + + @pytest.mark.asyncio async def test_list_documents_async( transport: str = "grpc_asyncio", request_type=document.ListDocumentsRequest @@ -813,6 +832,22 @@ def test_get_document_from_dict(): test_get_document(request_type=dict) +def test_get_document_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_document), "__call__") as call: + client.get_document() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == document.GetDocumentRequest() + + @pytest.mark.asyncio async def test_get_document_async( transport: str = "grpc_asyncio", request_type=document.GetDocumentRequest @@ -1011,6 +1046,22 @@ def test_create_document_from_dict(): test_create_document(request_type=dict) +def test_create_document_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_document), "__call__") as call: + client.create_document() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_document.CreateDocumentRequest() + + @pytest.mark.asyncio async def test_create_document_async( transport: str = "grpc_asyncio", request_type=gcd_document.CreateDocumentRequest @@ -1177,6 +1228,141 @@ async def test_create_document_flattened_error_async(): ) +def test_import_documents( + transport: str = "grpc", request_type=document.ImportDocumentsRequest +): + client = DocumentsClient( + 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.import_documents), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + + response = client.import_documents(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == document.ImportDocumentsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_import_documents_from_dict(): + test_import_documents(request_type=dict) + + +def test_import_documents_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.import_documents), "__call__") as call: + client.import_documents() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == document.ImportDocumentsRequest() + + +@pytest.mark.asyncio +async def test_import_documents_async( + transport: str = "grpc_asyncio", request_type=document.ImportDocumentsRequest +): + client = DocumentsAsyncClient( + 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.import_documents), "__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.import_documents(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == document.ImportDocumentsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_import_documents_async_from_dict(): + await test_import_documents_async(request_type=dict) + + +def test_import_documents_field_headers(): + client = DocumentsClient(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 = document.ImportDocumentsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.import_documents), "__call__") as call: + call.return_value = operations_pb2.Operation(name="operations/op") + + client.import_documents(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_import_documents_field_headers_async(): + client = DocumentsAsyncClient(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 = document.ImportDocumentsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.import_documents), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + + await client.import_documents(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_delete_document( transport: str = "grpc", request_type=document.DeleteDocumentRequest ): @@ -1209,6 +1395,22 @@ def test_delete_document_from_dict(): test_delete_document(request_type=dict) +def test_delete_document_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_document), "__call__") as call: + client.delete_document() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == document.DeleteDocumentRequest() + + @pytest.mark.asyncio async def test_delete_document_async( transport: str = "grpc_asyncio", request_type=document.DeleteDocumentRequest @@ -1395,6 +1597,22 @@ def test_update_document_from_dict(): test_update_document(request_type=dict) +def test_update_document_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_document), "__call__") as call: + client.update_document() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_document.UpdateDocumentRequest() + + @pytest.mark.asyncio async def test_update_document_async( transport: str = "grpc_asyncio", request_type=gcd_document.UpdateDocumentRequest @@ -1599,6 +1817,22 @@ def test_reload_document_from_dict(): test_reload_document(request_type=dict) +def test_reload_document_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = DocumentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.reload_document), "__call__") as call: + client.reload_document() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == document.ReloadDocumentRequest() + + @pytest.mark.asyncio async def test_reload_document_async( transport: str = "grpc_asyncio", request_type=document.ReloadDocumentRequest @@ -1821,7 +2055,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.DocumentsGrpcTransport, transports.DocumentsGrpcAsyncIOTransport], + [transports.DocumentsGrpcTransport, transports.DocumentsGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -1862,6 +2096,7 @@ def test_documents_base_transport(): "list_documents", "get_document", "create_document", + "import_documents", "delete_document", "update_document", "reload_document", @@ -1940,6 +2175,51 @@ def test_documents_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.DocumentsGrpcTransport, transports.DocumentsGrpcAsyncIOTransport], +) +def test_documents_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_documents_host_no_port(): client = DocumentsClient( credentials=credentials.AnonymousCredentials(), @@ -1961,7 +2241,7 @@ def test_documents_host_with_port(): def test_documents_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.DocumentsGrpcTransport( @@ -1973,7 +2253,7 @@ def test_documents_grpc_transport_channel(): def test_documents_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.DocumentsGrpcAsyncIOTransport( @@ -1984,6 +2264,8 @@ def test_documents_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.DocumentsGrpcTransport, transports.DocumentsGrpcAsyncIOTransport], @@ -1993,7 +2275,7 @@ def test_documents_transport_channel_mtls_with_client_cert_source(transport_clas "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -2034,6 +2316,8 @@ def test_documents_transport_channel_mtls_with_client_cert_source(transport_clas assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.DocumentsGrpcTransport, transports.DocumentsGrpcAsyncIOTransport], @@ -2046,7 +2330,7 @@ def test_documents_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2beta1/test_entity_types.py b/tests/unit/gapic/dialogflow_v2beta1/test_entity_types.py index 7594b4ce0..84f937bcb 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/test_entity_types.py +++ b/tests/unit/gapic/dialogflow_v2beta1/test_entity_types.py @@ -88,7 +88,22 @@ def test__get_default_mtls_endpoint(): assert EntityTypesClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [EntityTypesClient, EntityTypesAsyncClient]) +@pytest.mark.parametrize("client_class", [EntityTypesClient, EntityTypesAsyncClient,]) +def test_entity_types_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@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( @@ -97,16 +112,21 @@ def test_entity_types_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_entity_types_client_get_transport_class(): transport = EntityTypesClient.get_transport_class() - assert transport == transports.EntityTypesGrpcTransport + available_transports = [ + transports.EntityTypesGrpcTransport, + ] + assert transport in available_transports transport = EntityTypesClient.get_transport_class("grpc") assert transport == transports.EntityTypesGrpcTransport @@ -155,7 +175,7 @@ def test_entity_types_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -171,7 +191,7 @@ def test_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -187,7 +207,7 @@ def test_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -215,7 +235,7 @@ def test_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -264,29 +284,25 @@ def test_entity_types_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -295,66 +311,53 @@ def test_entity_types_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -380,7 +383,7 @@ def test_entity_types_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -410,7 +413,7 @@ def test_entity_types_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -427,7 +430,7 @@ def test_entity_types_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -472,6 +475,24 @@ def test_list_entity_types_from_dict(): test_list_entity_types(request_type=dict) +def test_list_entity_types_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_entity_types), "__call__" + ) as call: + client.list_entity_types() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.ListEntityTypesRequest() + + @pytest.mark.asyncio async def test_list_entity_types_async( transport: str = "grpc_asyncio", request_type=entity_type.ListEntityTypesRequest @@ -858,6 +879,22 @@ def test_get_entity_type_from_dict(): test_get_entity_type(request_type=dict) +def test_get_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_entity_type), "__call__") as call: + client.get_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.GetEntityTypeRequest() + + @pytest.mark.asyncio async def test_get_entity_type_async( transport: str = "grpc_asyncio", request_type=entity_type.GetEntityTypeRequest @@ -1097,6 +1134,24 @@ def test_create_entity_type_from_dict(): test_create_entity_type(request_type=dict) +def test_create_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.create_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_entity_type.CreateEntityTypeRequest() + + @pytest.mark.asyncio async def test_create_entity_type_async( transport: str = "grpc_asyncio", @@ -1357,6 +1412,24 @@ def test_update_entity_type_from_dict(): test_update_entity_type(request_type=dict) +def test_update_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.update_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_entity_type.UpdateEntityTypeRequest() + + @pytest.mark.asyncio async def test_update_entity_type_async( transport: str = "grpc_asyncio", @@ -1601,6 +1674,24 @@ def test_delete_entity_type_from_dict(): test_delete_entity_type(request_type=dict) +def test_delete_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.delete_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.DeleteEntityTypeRequest() + + @pytest.mark.asyncio async def test_delete_entity_type_async( transport: str = "grpc_asyncio", request_type=entity_type.DeleteEntityTypeRequest @@ -1793,6 +1884,24 @@ def test_batch_update_entity_types_from_dict(): test_batch_update_entity_types(request_type=dict) +def test_batch_update_entity_types_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_update_entity_types), "__call__" + ) as call: + client.batch_update_entity_types() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.BatchUpdateEntityTypesRequest() + + @pytest.mark.asyncio async def test_batch_update_entity_types_async( transport: str = "grpc_asyncio", @@ -1921,6 +2030,24 @@ def test_batch_delete_entity_types_from_dict(): test_batch_delete_entity_types(request_type=dict) +def test_batch_delete_entity_types_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_delete_entity_types), "__call__" + ) as call: + client.batch_delete_entity_types() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.BatchDeleteEntityTypesRequest() + + @pytest.mark.asyncio async def test_batch_delete_entity_types_async( transport: str = "grpc_asyncio", @@ -2132,6 +2259,24 @@ def test_batch_create_entities_from_dict(): test_batch_create_entities(request_type=dict) +def test_batch_create_entities_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_create_entities), "__call__" + ) as call: + client.batch_create_entities() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.BatchCreateEntitiesRequest() + + @pytest.mark.asyncio async def test_batch_create_entities_async( transport: str = "grpc_asyncio", request_type=entity_type.BatchCreateEntitiesRequest @@ -2352,6 +2497,24 @@ def test_batch_update_entities_from_dict(): test_batch_update_entities(request_type=dict) +def test_batch_update_entities_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_update_entities), "__call__" + ) as call: + client.batch_update_entities() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.BatchUpdateEntitiesRequest() + + @pytest.mark.asyncio async def test_batch_update_entities_async( transport: str = "grpc_asyncio", request_type=entity_type.BatchUpdateEntitiesRequest @@ -2572,6 +2735,24 @@ def test_batch_delete_entities_from_dict(): test_batch_delete_entities(request_type=dict) +def test_batch_delete_entities_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_delete_entities), "__call__" + ) as call: + client.batch_delete_entities() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == entity_type.BatchDeleteEntitiesRequest() + + @pytest.mark.asyncio async def test_batch_delete_entities_async( transport: str = "grpc_asyncio", request_type=entity_type.BatchDeleteEntitiesRequest @@ -2814,7 +2995,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], + [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -2937,6 +3118,51 @@ def test_entity_types_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], +) +def test_entity_types_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_entity_types_host_no_port(): client = EntityTypesClient( credentials=credentials.AnonymousCredentials(), @@ -2958,7 +3184,7 @@ def test_entity_types_host_with_port(): def test_entity_types_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.EntityTypesGrpcTransport( @@ -2970,7 +3196,7 @@ def test_entity_types_grpc_transport_channel(): def test_entity_types_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.EntityTypesGrpcAsyncIOTransport( @@ -2981,6 +3207,8 @@ def test_entity_types_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], @@ -2990,7 +3218,7 @@ def test_entity_types_transport_channel_mtls_with_client_cert_source(transport_c "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -3031,6 +3259,8 @@ def test_entity_types_transport_channel_mtls_with_client_cert_source(transport_c assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.EntityTypesGrpcTransport, transports.EntityTypesGrpcAsyncIOTransport], @@ -3043,7 +3273,7 @@ def test_entity_types_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2beta1/test_environments.py b/tests/unit/gapic/dialogflow_v2beta1/test_environments.py index 93d74156a..78bcf4104 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/test_environments.py +++ b/tests/unit/gapic/dialogflow_v2beta1/test_environments.py @@ -83,7 +83,22 @@ def test__get_default_mtls_endpoint(): assert EnvironmentsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [EnvironmentsClient, EnvironmentsAsyncClient]) +@pytest.mark.parametrize("client_class", [EnvironmentsClient, EnvironmentsAsyncClient,]) +def test_environments_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [EnvironmentsClient, EnvironmentsAsyncClient,]) def test_environments_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -92,16 +107,21 @@ def test_environments_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_environments_client_get_transport_class(): transport = EnvironmentsClient.get_transport_class() - assert transport == transports.EnvironmentsGrpcTransport + available_transports = [ + transports.EnvironmentsGrpcTransport, + ] + assert transport in available_transports transport = EnvironmentsClient.get_transport_class("grpc") assert transport == transports.EnvironmentsGrpcTransport @@ -150,7 +170,7 @@ def test_environments_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -166,7 +186,7 @@ def test_environments_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -182,7 +202,7 @@ def test_environments_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -210,7 +230,7 @@ def test_environments_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -259,29 +279,25 @@ def test_environments_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -290,66 +306,53 @@ def test_environments_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -375,7 +378,7 @@ def test_environments_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -405,7 +408,7 @@ def test_environments_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -422,7 +425,7 @@ def test_environments_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -467,6 +470,24 @@ def test_list_environments_from_dict(): test_list_environments(request_type=dict) +def test_list_environments_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = EnvironmentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_environments), "__call__" + ) as call: + client.list_environments() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == environment.ListEnvironmentsRequest() + + @pytest.mark.asyncio async def test_list_environments_async( transport: str = "grpc_asyncio", request_type=environment.ListEnvironmentsRequest @@ -845,7 +866,10 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], + [ + transports.EnvironmentsGrpcTransport, + transports.EnvironmentsGrpcAsyncIOTransport, + ], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -952,6 +976,51 @@ def test_environments_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], +) +def test_environments_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_environments_host_no_port(): client = EnvironmentsClient( credentials=credentials.AnonymousCredentials(), @@ -973,7 +1042,7 @@ def test_environments_host_with_port(): def test_environments_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.EnvironmentsGrpcTransport( @@ -985,7 +1054,7 @@ def test_environments_grpc_transport_channel(): def test_environments_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.EnvironmentsGrpcAsyncIOTransport( @@ -996,6 +1065,8 @@ def test_environments_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], @@ -1005,7 +1076,7 @@ def test_environments_transport_channel_mtls_with_client_cert_source(transport_c "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -1046,6 +1117,8 @@ def test_environments_transport_channel_mtls_with_client_cert_source(transport_c assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.EnvironmentsGrpcTransport, transports.EnvironmentsGrpcAsyncIOTransport], @@ -1058,7 +1131,7 @@ def test_environments_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2beta1/test_intents.py b/tests/unit/gapic/dialogflow_v2beta1/test_intents.py index 9f75aa954..c922cc547 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/test_intents.py +++ b/tests/unit/gapic/dialogflow_v2beta1/test_intents.py @@ -86,7 +86,22 @@ def test__get_default_mtls_endpoint(): assert IntentsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [IntentsClient, IntentsAsyncClient]) +@pytest.mark.parametrize("client_class", [IntentsClient, IntentsAsyncClient,]) +def test_intents_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [IntentsClient, IntentsAsyncClient,]) def test_intents_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -95,16 +110,21 @@ def test_intents_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_intents_client_get_transport_class(): transport = IntentsClient.get_transport_class() - assert transport == transports.IntentsGrpcTransport + available_transports = [ + transports.IntentsGrpcTransport, + ] + assert transport in available_transports transport = IntentsClient.get_transport_class("grpc") assert transport == transports.IntentsGrpcTransport @@ -145,7 +165,7 @@ def test_intents_client_client_options(client_class, transport_class, transport_ credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -161,7 +181,7 @@ def test_intents_client_client_options(client_class, transport_class, transport_ credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -177,7 +197,7 @@ def test_intents_client_client_options(client_class, transport_class, transport_ credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -205,7 +225,7 @@ def test_intents_client_client_options(client_class, transport_class, transport_ credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -252,29 +272,25 @@ def test_intents_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -283,66 +299,53 @@ def test_intents_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -364,7 +367,7 @@ def test_intents_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -390,7 +393,7 @@ def test_intents_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -407,7 +410,7 @@ def test_intents_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -448,6 +451,22 @@ def test_list_intents_from_dict(): test_list_intents(request_type=dict) +def test_list_intents_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_intents), "__call__") as call: + client.list_intents() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.ListIntentsRequest() + + @pytest.mark.asyncio async def test_list_intents_async( transport: str = "grpc_asyncio", request_type=intent.ListIntentsRequest @@ -750,6 +769,7 @@ def test_get_intent(transport: str = "grpc", request_type=intent.GetIntentReques is_fallback=True, ml_enabled=True, ml_disabled=True, + live_agent_handoff=True, end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], @@ -786,6 +806,8 @@ def test_get_intent(transport: str = "grpc", request_type=intent.GetIntentReques assert response.ml_disabled is True + assert response.live_agent_handoff is True + assert response.end_interaction is True assert response.input_context_names == ["input_context_names_value"] @@ -809,6 +831,22 @@ def test_get_intent_from_dict(): test_get_intent(request_type=dict) +def test_get_intent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_intent), "__call__") as call: + client.get_intent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.GetIntentRequest() + + @pytest.mark.asyncio async def test_get_intent_async( transport: str = "grpc_asyncio", request_type=intent.GetIntentRequest @@ -833,6 +871,7 @@ async def test_get_intent_async( is_fallback=True, ml_enabled=True, ml_disabled=True, + live_agent_handoff=True, end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], @@ -869,6 +908,8 @@ async def test_get_intent_async( assert response.ml_disabled is True + assert response.live_agent_handoff is True + assert response.end_interaction is True assert response.input_context_names == ["input_context_names_value"] @@ -1041,6 +1082,7 @@ def test_create_intent( is_fallback=True, ml_enabled=True, ml_disabled=True, + live_agent_handoff=True, end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], @@ -1079,6 +1121,8 @@ def test_create_intent( assert response.ml_disabled is True + assert response.live_agent_handoff is True + assert response.end_interaction is True assert response.input_context_names == ["input_context_names_value"] @@ -1102,6 +1146,22 @@ def test_create_intent_from_dict(): test_create_intent(request_type=dict) +def test_create_intent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_intent), "__call__") as call: + client.create_intent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_intent.CreateIntentRequest() + + @pytest.mark.asyncio async def test_create_intent_async( transport: str = "grpc_asyncio", request_type=gcd_intent.CreateIntentRequest @@ -1126,6 +1186,7 @@ async def test_create_intent_async( is_fallback=True, ml_enabled=True, ml_disabled=True, + live_agent_handoff=True, end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], @@ -1166,6 +1227,8 @@ async def test_create_intent_async( assert response.ml_disabled is True + assert response.live_agent_handoff is True + assert response.end_interaction is True assert response.input_context_names == ["input_context_names_value"] @@ -1348,6 +1411,7 @@ def test_update_intent( is_fallback=True, ml_enabled=True, ml_disabled=True, + live_agent_handoff=True, end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], @@ -1386,6 +1450,8 @@ def test_update_intent( assert response.ml_disabled is True + assert response.live_agent_handoff is True + assert response.end_interaction is True assert response.input_context_names == ["input_context_names_value"] @@ -1409,6 +1475,22 @@ def test_update_intent_from_dict(): test_update_intent(request_type=dict) +def test_update_intent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_intent), "__call__") as call: + client.update_intent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_intent.UpdateIntentRequest() + + @pytest.mark.asyncio async def test_update_intent_async( transport: str = "grpc_asyncio", request_type=gcd_intent.UpdateIntentRequest @@ -1433,6 +1515,7 @@ async def test_update_intent_async( is_fallback=True, ml_enabled=True, ml_disabled=True, + live_agent_handoff=True, end_interaction=True, input_context_names=["input_context_names_value"], events=["events_value"], @@ -1473,6 +1556,8 @@ async def test_update_intent_async( assert response.ml_disabled is True + assert response.live_agent_handoff is True + assert response.end_interaction is True assert response.input_context_names == ["input_context_names_value"] @@ -1665,6 +1750,22 @@ def test_delete_intent_from_dict(): test_delete_intent(request_type=dict) +def test_delete_intent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_intent), "__call__") as call: + client.delete_intent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.DeleteIntentRequest() + + @pytest.mark.asyncio async def test_delete_intent_async( transport: str = "grpc_asyncio", request_type=intent.DeleteIntentRequest @@ -1847,6 +1948,24 @@ def test_batch_update_intents_from_dict(): test_batch_update_intents(request_type=dict) +def test_batch_update_intents_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_update_intents), "__call__" + ) as call: + client.batch_update_intents() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.BatchUpdateIntentsRequest() + + @pytest.mark.asyncio async def test_batch_update_intents_async( transport: str = "grpc_asyncio", request_type=intent.BatchUpdateIntentsRequest @@ -2075,6 +2194,24 @@ def test_batch_delete_intents_from_dict(): test_batch_delete_intents(request_type=dict) +def test_batch_delete_intents_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = IntentsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.batch_delete_intents), "__call__" + ) as call: + client.batch_delete_intents() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == intent.BatchDeleteIntentsRequest() + + @pytest.mark.asyncio async def test_batch_delete_intents_async( transport: str = "grpc_asyncio", request_type=intent.BatchDeleteIntentsRequest @@ -2307,7 +2444,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], + [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -2427,6 +2564,51 @@ def test_intents_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], +) +def test_intents_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_intents_host_no_port(): client = IntentsClient( credentials=credentials.AnonymousCredentials(), @@ -2448,7 +2630,7 @@ def test_intents_host_with_port(): def test_intents_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.IntentsGrpcTransport( @@ -2460,7 +2642,7 @@ def test_intents_grpc_transport_channel(): def test_intents_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.IntentsGrpcAsyncIOTransport( @@ -2471,6 +2653,8 @@ def test_intents_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], @@ -2480,7 +2664,7 @@ def test_intents_transport_channel_mtls_with_client_cert_source(transport_class) "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -2521,6 +2705,8 @@ def test_intents_transport_channel_mtls_with_client_cert_source(transport_class) assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.IntentsGrpcTransport, transports.IntentsGrpcAsyncIOTransport], @@ -2533,7 +2719,7 @@ def test_intents_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2beta1/test_knowledge_bases.py b/tests/unit/gapic/dialogflow_v2beta1/test_knowledge_bases.py index 1d3988feb..0e365bc42 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/test_knowledge_bases.py +++ b/tests/unit/gapic/dialogflow_v2beta1/test_knowledge_bases.py @@ -91,7 +91,24 @@ def test__get_default_mtls_endpoint(): @pytest.mark.parametrize( - "client_class", [KnowledgeBasesClient, KnowledgeBasesAsyncClient] + "client_class", [KnowledgeBasesClient, KnowledgeBasesAsyncClient,] +) +def test_knowledge_bases_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize( + "client_class", [KnowledgeBasesClient, KnowledgeBasesAsyncClient,] ) def test_knowledge_bases_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() @@ -101,16 +118,21 @@ def test_knowledge_bases_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_knowledge_bases_client_get_transport_class(): transport = KnowledgeBasesClient.get_transport_class() - assert transport == transports.KnowledgeBasesGrpcTransport + available_transports = [ + transports.KnowledgeBasesGrpcTransport, + ] + assert transport in available_transports transport = KnowledgeBasesClient.get_transport_class("grpc") assert transport == transports.KnowledgeBasesGrpcTransport @@ -161,7 +183,7 @@ def test_knowledge_bases_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -177,7 +199,7 @@ def test_knowledge_bases_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -193,7 +215,7 @@ def test_knowledge_bases_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -221,7 +243,7 @@ def test_knowledge_bases_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -272,29 +294,25 @@ def test_knowledge_bases_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -303,66 +321,53 @@ def test_knowledge_bases_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -388,7 +393,7 @@ def test_knowledge_bases_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -418,7 +423,7 @@ def test_knowledge_bases_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -437,7 +442,7 @@ def test_knowledge_bases_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -482,6 +487,24 @@ def test_list_knowledge_bases_from_dict(): test_list_knowledge_bases(request_type=dict) +def test_list_knowledge_bases_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_knowledge_bases), "__call__" + ) as call: + client.list_knowledge_bases() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.ListKnowledgeBasesRequest() + + @pytest.mark.asyncio async def test_list_knowledge_bases_async( transport: str = "grpc_asyncio", @@ -866,6 +889,24 @@ def test_get_knowledge_base_from_dict(): test_get_knowledge_base(request_type=dict) +def test_get_knowledge_base_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_knowledge_base), "__call__" + ) as call: + client.get_knowledge_base() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.GetKnowledgeBaseRequest() + + @pytest.mark.asyncio async def test_get_knowledge_base_async( transport: str = "grpc_asyncio", request_type=knowledge_base.GetKnowledgeBaseRequest @@ -1085,6 +1126,24 @@ def test_create_knowledge_base_from_dict(): test_create_knowledge_base(request_type=dict) +def test_create_knowledge_base_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_knowledge_base), "__call__" + ) as call: + client.create_knowledge_base() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_knowledge_base.CreateKnowledgeBaseRequest() + + @pytest.mark.asyncio async def test_create_knowledge_base_async( transport: str = "grpc_asyncio", @@ -1312,6 +1371,24 @@ def test_delete_knowledge_base_from_dict(): test_delete_knowledge_base(request_type=dict) +def test_delete_knowledge_base_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.delete_knowledge_base), "__call__" + ) as call: + client.delete_knowledge_base() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == knowledge_base.DeleteKnowledgeBaseRequest() + + @pytest.mark.asyncio async def test_delete_knowledge_base_async( transport: str = "grpc_asyncio", @@ -1516,6 +1593,24 @@ def test_update_knowledge_base_from_dict(): test_update_knowledge_base(request_type=dict) +def test_update_knowledge_base_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = KnowledgeBasesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_knowledge_base), "__call__" + ) as call: + client.update_knowledge_base() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_knowledge_base.UpdateKnowledgeBaseRequest() + + @pytest.mark.asyncio async def test_update_knowledge_base_async( transport: str = "grpc_asyncio", @@ -1887,6 +1982,54 @@ def test_knowledge_bases_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.KnowledgeBasesGrpcTransport, + transports.KnowledgeBasesGrpcAsyncIOTransport, + ], +) +def test_knowledge_bases_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_knowledge_bases_host_no_port(): client = KnowledgeBasesClient( credentials=credentials.AnonymousCredentials(), @@ -1908,7 +2051,7 @@ def test_knowledge_bases_host_with_port(): def test_knowledge_bases_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.KnowledgeBasesGrpcTransport( @@ -1920,7 +2063,7 @@ def test_knowledge_bases_grpc_transport_channel(): def test_knowledge_bases_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.KnowledgeBasesGrpcAsyncIOTransport( @@ -1931,6 +2074,8 @@ def test_knowledge_bases_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [ @@ -1945,7 +2090,7 @@ def test_knowledge_bases_transport_channel_mtls_with_client_cert_source( "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -1986,6 +2131,8 @@ def test_knowledge_bases_transport_channel_mtls_with_client_cert_source( assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [ @@ -2001,7 +2148,7 @@ def test_knowledge_bases_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2beta1/test_participants.py b/tests/unit/gapic/dialogflow_v2beta1/test_participants.py new file mode 100644 index 000000000..8887c027c --- /dev/null +++ b/tests/unit/gapic/dialogflow_v2beta1/test_participants.py @@ -0,0 +1,3627 @@ +# -*- 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.dialogflow_v2beta1.services.participants import ( + ParticipantsAsyncClient, +) +from google.cloud.dialogflow_v2beta1.services.participants import ParticipantsClient +from google.cloud.dialogflow_v2beta1.services.participants import pagers +from google.cloud.dialogflow_v2beta1.services.participants import transports +from google.cloud.dialogflow_v2beta1.types import agent +from google.cloud.dialogflow_v2beta1.types import audio_config +from google.cloud.dialogflow_v2beta1.types import context +from google.cloud.dialogflow_v2beta1.types import entity_type +from google.cloud.dialogflow_v2beta1.types import participant +from google.cloud.dialogflow_v2beta1.types import participant as gcd_participant +from google.cloud.dialogflow_v2beta1.types import session +from google.cloud.dialogflow_v2beta1.types import session_entity_type +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 +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 ParticipantsClient._get_default_mtls_endpoint(None) is None + assert ( + ParticipantsClient._get_default_mtls_endpoint(api_endpoint) == api_mtls_endpoint + ) + assert ( + ParticipantsClient._get_default_mtls_endpoint(api_mtls_endpoint) + == api_mtls_endpoint + ) + assert ( + ParticipantsClient._get_default_mtls_endpoint(sandbox_endpoint) + == sandbox_mtls_endpoint + ) + assert ( + ParticipantsClient._get_default_mtls_endpoint(sandbox_mtls_endpoint) + == sandbox_mtls_endpoint + ) + assert ParticipantsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi + + +@pytest.mark.parametrize("client_class", [ParticipantsClient, ParticipantsAsyncClient,]) +def test_participants_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [ParticipantsClient, ParticipantsAsyncClient,]) +def test_participants_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 + assert isinstance(client, client_class) + + client = client_class.from_service_account_json("dummy/file/path.json") + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_participants_client_get_transport_class(): + transport = ParticipantsClient.get_transport_class() + available_transports = [ + transports.ParticipantsGrpcTransport, + ] + assert transport in available_transports + + transport = ParticipantsClient.get_transport_class("grpc") + assert transport == transports.ParticipantsGrpcTransport + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ParticipantsClient, transports.ParticipantsGrpcTransport, "grpc"), + ( + ParticipantsAsyncClient, + transports.ParticipantsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +@mock.patch.object( + ParticipantsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(ParticipantsClient) +) +@mock.patch.object( + ParticipantsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ParticipantsAsyncClient), +) +def test_participants_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(ParticipantsClient, "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(ParticipantsClient, "get_transport_class") as gtc: + client = client_class(transport=transport_name) + gtc.assert_called() + + # Check the case api_endpoint is provided. + options = client_options.ClientOptions(api_endpoint="squid.clam.whelk") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "never". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "never"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT is + # "always". + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "always"}): + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_MTLS_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case api_endpoint is not provided and GOOGLE_API_USE_MTLS_ENDPOINT has + # unsupported value. + with mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "Unsupported"}): + with pytest.raises(MutualTLSChannelError): + client = client_class() + + # Check the case GOOGLE_API_USE_CLIENT_CERTIFICATE has unsupported value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": "Unsupported"} + ): + with pytest.raises(ValueError): + client = client_class() + + # Check the case quota_project_id is provided + options = client_options.ClientOptions(quota_project_id="octopus") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id="octopus", + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name,use_client_cert_env", + [ + (ParticipantsClient, transports.ParticipantsGrpcTransport, "grpc", "true"), + ( + ParticipantsAsyncClient, + transports.ParticipantsGrpcAsyncIOTransport, + "grpc_asyncio", + "true", + ), + (ParticipantsClient, transports.ParticipantsGrpcTransport, "grpc", "false"), + ( + ParticipantsAsyncClient, + transports.ParticipantsGrpcAsyncIOTransport, + "grpc_asyncio", + "false", + ), + ], +) +@mock.patch.object( + ParticipantsClient, "DEFAULT_ENDPOINT", modify_default_endpoint(ParticipantsClient) +) +@mock.patch.object( + ParticipantsAsyncClient, + "DEFAULT_ENDPOINT", + modify_default_endpoint(ParticipantsAsyncClient), +) +@mock.patch.dict(os.environ, {"GOOGLE_API_USE_MTLS_ENDPOINT": "auto"}) +def test_participants_client_mtls_env_auto( + client_class, transport_class, transport_name, use_client_cert_env +): + # This tests the endpoint autoswitch behavior. Endpoint is autoswitched to the default + # mtls endpoint, if GOOGLE_API_USE_CLIENT_CERTIFICATE is "true" and client cert exists. + + # Check the case client_cert_source is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + options = client_options.ClientOptions( + client_cert_source=client_cert_source_callback + ) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT + + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case ADC client cert is provided. Whether client cert is used depends on + # GOOGLE_API_USE_CLIENT_CERTIFICATE value. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, + ): + with mock.patch( + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback + + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ParticipantsClient, transports.ParticipantsGrpcTransport, "grpc"), + ( + ParticipantsAsyncClient, + transports.ParticipantsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_participants_client_client_options_scopes( + client_class, transport_class, transport_name +): + # Check the case scopes are provided. + options = client_options.ClientOptions(scopes=["1", "2"],) + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=["1", "2"], + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +@pytest.mark.parametrize( + "client_class,transport_class,transport_name", + [ + (ParticipantsClient, transports.ParticipantsGrpcTransport, "grpc"), + ( + ParticipantsAsyncClient, + transports.ParticipantsGrpcAsyncIOTransport, + "grpc_asyncio", + ), + ], +) +def test_participants_client_client_options_credentials_file( + client_class, transport_class, transport_name +): + # Check the case credentials file is provided. + options = client_options.ClientOptions(credentials_file="credentials.json") + with mock.patch.object(transport_class, "__init__") as patched: + patched.return_value = None + client = client_class(client_options=options) + patched.assert_called_once_with( + credentials=None, + credentials_file="credentials.json", + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_participants_client_client_options_from_dict(): + with mock.patch( + "google.cloud.dialogflow_v2beta1.services.participants.transports.ParticipantsGrpcTransport.__init__" + ) as grpc_transport: + grpc_transport.return_value = None + client = ParticipantsClient(client_options={"api_endpoint": "squid.clam.whelk"}) + grpc_transport.assert_called_once_with( + credentials=None, + credentials_file=None, + host="squid.clam.whelk", + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + + +def test_create_participant( + transport: str = "grpc", request_type=gcd_participant.CreateParticipantRequest +): + client = ParticipantsClient( + 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_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant( + name="name_value", + role=gcd_participant.Participant.Role.HUMAN_AGENT, + obfuscated_external_user_id="obfuscated_external_user_id_value", + ) + + response = client.create_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.CreateParticipantRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_participant.Participant) + + assert response.name == "name_value" + + assert response.role == gcd_participant.Participant.Role.HUMAN_AGENT + + assert response.obfuscated_external_user_id == "obfuscated_external_user_id_value" + + +def test_create_participant_from_dict(): + test_create_participant(request_type=dict) + + +def test_create_participant_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_participant), "__call__" + ) as call: + client.create_participant() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.CreateParticipantRequest() + + +@pytest.mark.asyncio +async def test_create_participant_async( + transport: str = "grpc_asyncio", + request_type=gcd_participant.CreateParticipantRequest, +): + client = ParticipantsAsyncClient( + 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_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant( + name="name_value", + role=gcd_participant.Participant.Role.HUMAN_AGENT, + obfuscated_external_user_id="obfuscated_external_user_id_value", + ) + ) + + response = await client.create_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.CreateParticipantRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_participant.Participant) + + assert response.name == "name_value" + + assert response.role == gcd_participant.Participant.Role.HUMAN_AGENT + + assert response.obfuscated_external_user_id == "obfuscated_external_user_id_value" + + +@pytest.mark.asyncio +async def test_create_participant_async_from_dict(): + await test_create_participant_async(request_type=dict) + + +def test_create_participant_field_headers(): + client = ParticipantsClient(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 = gcd_participant.CreateParticipantRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_participant), "__call__" + ) as call: + call.return_value = gcd_participant.Participant() + + client.create_participant(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_participant_field_headers_async(): + client = ParticipantsAsyncClient(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 = gcd_participant.CreateParticipantRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_participant), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant() + ) + + await client.create_participant(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_participant_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.create_participant( + parent="parent_value", + participant=gcd_participant.Participant(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].participant == gcd_participant.Participant(name="name_value") + + +def test_create_participant_flattened_error(): + client = ParticipantsClient(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_participant( + gcd_participant.CreateParticipantRequest(), + parent="parent_value", + participant=gcd_participant.Participant(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_create_participant_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.create_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.create_participant( + parent="parent_value", + participant=gcd_participant.Participant(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].participant == gcd_participant.Participant(name="name_value") + + +@pytest.mark.asyncio +async def test_create_participant_flattened_error_async(): + client = ParticipantsAsyncClient(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_participant( + gcd_participant.CreateParticipantRequest(), + parent="parent_value", + participant=gcd_participant.Participant(name="name_value"), + ) + + +def test_get_participant( + transport: str = "grpc", request_type=participant.GetParticipantRequest +): + client = ParticipantsClient( + 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_participant), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.Participant( + name="name_value", + role=participant.Participant.Role.HUMAN_AGENT, + obfuscated_external_user_id="obfuscated_external_user_id_value", + ) + + response = client.get_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.GetParticipantRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, participant.Participant) + + assert response.name == "name_value" + + assert response.role == participant.Participant.Role.HUMAN_AGENT + + assert response.obfuscated_external_user_id == "obfuscated_external_user_id_value" + + +def test_get_participant_from_dict(): + test_get_participant(request_type=dict) + + +def test_get_participant_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_participant), "__call__") as call: + client.get_participant() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.GetParticipantRequest() + + +@pytest.mark.asyncio +async def test_get_participant_async( + transport: str = "grpc_asyncio", request_type=participant.GetParticipantRequest +): + client = ParticipantsAsyncClient( + 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_participant), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.Participant( + name="name_value", + role=participant.Participant.Role.HUMAN_AGENT, + obfuscated_external_user_id="obfuscated_external_user_id_value", + ) + ) + + response = await client.get_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.GetParticipantRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, participant.Participant) + + assert response.name == "name_value" + + assert response.role == participant.Participant.Role.HUMAN_AGENT + + assert response.obfuscated_external_user_id == "obfuscated_external_user_id_value" + + +@pytest.mark.asyncio +async def test_get_participant_async_from_dict(): + await test_get_participant_async(request_type=dict) + + +def test_get_participant_field_headers(): + client = ParticipantsClient(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 = participant.GetParticipantRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_participant), "__call__") as call: + call.return_value = participant.Participant() + + client.get_participant(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_participant_field_headers_async(): + client = ParticipantsAsyncClient(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 = participant.GetParticipantRequest() + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_participant), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.Participant() + ) + + await client.get_participant(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_participant_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_participant), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.Participant() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_participant(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_participant_flattened_error(): + client = ParticipantsClient(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_participant( + participant.GetParticipantRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_participant_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_participant), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.Participant() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.Participant() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_participant(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_participant_flattened_error_async(): + client = ParticipantsAsyncClient(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_participant( + participant.GetParticipantRequest(), name="name_value", + ) + + +def test_list_participants( + transport: str = "grpc", request_type=participant.ListParticipantsRequest +): + client = ParticipantsClient( + 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_participants), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.ListParticipantsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_participants(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.ListParticipantsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListParticipantsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_participants_from_dict(): + test_list_participants(request_type=dict) + + +def test_list_participants_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + client.list_participants() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.ListParticipantsRequest() + + +@pytest.mark.asyncio +async def test_list_participants_async( + transport: str = "grpc_asyncio", request_type=participant.ListParticipantsRequest +): + client = ParticipantsAsyncClient( + 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_participants), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.ListParticipantsResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_participants(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.ListParticipantsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListParticipantsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_participants_async_from_dict(): + await test_list_participants_async(request_type=dict) + + +def test_list_participants_field_headers(): + client = ParticipantsClient(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 = participant.ListParticipantsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + call.return_value = participant.ListParticipantsResponse() + + client.list_participants(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_participants_field_headers_async(): + client = ParticipantsAsyncClient(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 = participant.ListParticipantsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.ListParticipantsResponse() + ) + + await client.list_participants(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_participants_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.ListParticipantsResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.list_participants(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_participants_flattened_error(): + client = ParticipantsClient(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_participants( + participant.ListParticipantsRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_list_participants_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.ListParticipantsResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.ListParticipantsResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.list_participants(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_participants_flattened_error_async(): + client = ParticipantsAsyncClient(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_participants( + participant.ListParticipantsRequest(), parent="parent_value", + ) + + +def test_list_participants_pager(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListParticipantsResponse( + participants=[ + participant.Participant(), + participant.Participant(), + participant.Participant(), + ], + next_page_token="abc", + ), + participant.ListParticipantsResponse( + participants=[], next_page_token="def", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(),], next_page_token="ghi", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(), participant.Participant(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_participants(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, participant.Participant) for i in results) + + +def test_list_participants_pages(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), "__call__" + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListParticipantsResponse( + participants=[ + participant.Participant(), + participant.Participant(), + participant.Participant(), + ], + next_page_token="abc", + ), + participant.ListParticipantsResponse( + participants=[], next_page_token="def", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(),], next_page_token="ghi", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(), participant.Participant(),], + ), + RuntimeError, + ) + pages = list(client.list_participants(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_participants_async_pager(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListParticipantsResponse( + participants=[ + participant.Participant(), + participant.Participant(), + participant.Participant(), + ], + next_page_token="abc", + ), + participant.ListParticipantsResponse( + participants=[], next_page_token="def", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(),], next_page_token="ghi", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(), participant.Participant(),], + ), + RuntimeError, + ) + async_pager = await client.list_participants(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, participant.Participant) for i in responses) + + +@pytest.mark.asyncio +async def test_list_participants_async_pages(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_participants), + "__call__", + new_callable=mock.AsyncMock, + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListParticipantsResponse( + participants=[ + participant.Participant(), + participant.Participant(), + participant.Participant(), + ], + next_page_token="abc", + ), + participant.ListParticipantsResponse( + participants=[], next_page_token="def", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(),], next_page_token="ghi", + ), + participant.ListParticipantsResponse( + participants=[participant.Participant(), participant.Participant(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_participants(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_update_participant( + transport: str = "grpc", request_type=gcd_participant.UpdateParticipantRequest +): + client = ParticipantsClient( + 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_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant( + name="name_value", + role=gcd_participant.Participant.Role.HUMAN_AGENT, + obfuscated_external_user_id="obfuscated_external_user_id_value", + ) + + response = client.update_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.UpdateParticipantRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_participant.Participant) + + assert response.name == "name_value" + + assert response.role == gcd_participant.Participant.Role.HUMAN_AGENT + + assert response.obfuscated_external_user_id == "obfuscated_external_user_id_value" + + +def test_update_participant_from_dict(): + test_update_participant(request_type=dict) + + +def test_update_participant_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_participant), "__call__" + ) as call: + client.update_participant() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.UpdateParticipantRequest() + + +@pytest.mark.asyncio +async def test_update_participant_async( + transport: str = "grpc_asyncio", + request_type=gcd_participant.UpdateParticipantRequest, +): + client = ParticipantsAsyncClient( + 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_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant( + name="name_value", + role=gcd_participant.Participant.Role.HUMAN_AGENT, + obfuscated_external_user_id="obfuscated_external_user_id_value", + ) + ) + + response = await client.update_participant(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.UpdateParticipantRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_participant.Participant) + + assert response.name == "name_value" + + assert response.role == gcd_participant.Participant.Role.HUMAN_AGENT + + assert response.obfuscated_external_user_id == "obfuscated_external_user_id_value" + + +@pytest.mark.asyncio +async def test_update_participant_async_from_dict(): + await test_update_participant_async(request_type=dict) + + +def test_update_participant_field_headers(): + client = ParticipantsClient(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 = gcd_participant.UpdateParticipantRequest() + request.participant.name = "participant.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_participant), "__call__" + ) as call: + call.return_value = gcd_participant.Participant() + + client.update_participant(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", "participant.name=participant.name/value",) in kw[ + "metadata" + ] + + +@pytest.mark.asyncio +async def test_update_participant_field_headers_async(): + client = ParticipantsAsyncClient(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 = gcd_participant.UpdateParticipantRequest() + request.participant.name = "participant.name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_participant), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant() + ) + + await client.update_participant(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", "participant.name=participant.name/value",) in kw[ + "metadata" + ] + + +def test_update_participant_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.update_participant( + participant=gcd_participant.Participant(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].participant == gcd_participant.Participant(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +def test_update_participant_flattened_error(): + client = ParticipantsClient(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_participant( + gcd_participant.UpdateParticipantRequest(), + participant=gcd_participant.Participant(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +@pytest.mark.asyncio +async def test_update_participant_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.update_participant), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.Participant() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.Participant() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.update_participant( + participant=gcd_participant.Participant(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].participant == gcd_participant.Participant(name="name_value") + + assert args[0].update_mask == field_mask.FieldMask(paths=["paths_value"]) + + +@pytest.mark.asyncio +async def test_update_participant_flattened_error_async(): + client = ParticipantsAsyncClient(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_participant( + gcd_participant.UpdateParticipantRequest(), + participant=gcd_participant.Participant(name="name_value"), + update_mask=field_mask.FieldMask(paths=["paths_value"]), + ) + + +def test_analyze_content( + transport: str = "grpc", request_type=gcd_participant.AnalyzeContentRequest +): + client = ParticipantsClient( + 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.analyze_content), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.AnalyzeContentResponse( + reply_text="reply_text_value", + ) + + response = client.analyze_content(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.AnalyzeContentRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, gcd_participant.AnalyzeContentResponse) + + assert response.reply_text == "reply_text_value" + + +def test_analyze_content_from_dict(): + test_analyze_content(request_type=dict) + + +def test_analyze_content_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.analyze_content), "__call__") as call: + client.analyze_content() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.AnalyzeContentRequest() + + +@pytest.mark.asyncio +async def test_analyze_content_async( + transport: str = "grpc_asyncio", request_type=gcd_participant.AnalyzeContentRequest +): + client = ParticipantsAsyncClient( + 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.analyze_content), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.AnalyzeContentResponse(reply_text="reply_text_value",) + ) + + response = await client.analyze_content(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_participant.AnalyzeContentRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, gcd_participant.AnalyzeContentResponse) + + assert response.reply_text == "reply_text_value" + + +@pytest.mark.asyncio +async def test_analyze_content_async_from_dict(): + await test_analyze_content_async(request_type=dict) + + +def test_analyze_content_field_headers(): + client = ParticipantsClient(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 = gcd_participant.AnalyzeContentRequest() + request.participant = "participant/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.analyze_content), "__call__") as call: + call.return_value = gcd_participant.AnalyzeContentResponse() + + client.analyze_content(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", "participant=participant/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_analyze_content_field_headers_async(): + client = ParticipantsAsyncClient(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 = gcd_participant.AnalyzeContentRequest() + request.participant = "participant/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.analyze_content), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.AnalyzeContentResponse() + ) + + await client.analyze_content(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", "participant=participant/value",) in kw["metadata"] + + +def test_analyze_content_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.analyze_content), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.AnalyzeContentResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.analyze_content( + participant="participant_value", + text_input=session.TextInput(text="text_value"), + audio_input=gcd_participant.AudioInput( + config=audio_config.InputAudioConfig( + audio_encoding=audio_config.AudioEncoding.AUDIO_ENCODING_LINEAR_16 + ) + ), + event_input=session.EventInput(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].participant == "participant_value" + + assert args[0].event_input == session.EventInput(name="name_value") + + +def test_analyze_content_flattened_error(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.analyze_content( + gcd_participant.AnalyzeContentRequest(), + participant="participant_value", + text_input=session.TextInput(text="text_value"), + audio_input=gcd_participant.AudioInput( + config=audio_config.InputAudioConfig( + audio_encoding=audio_config.AudioEncoding.AUDIO_ENCODING_LINEAR_16 + ) + ), + event_input=session.EventInput(name="name_value"), + ) + + +@pytest.mark.asyncio +async def test_analyze_content_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.analyze_content), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = gcd_participant.AnalyzeContentResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + gcd_participant.AnalyzeContentResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.analyze_content( + participant="participant_value", + text_input=session.TextInput(text="text_value"), + audio_input=gcd_participant.AudioInput( + config=audio_config.InputAudioConfig( + audio_encoding=audio_config.AudioEncoding.AUDIO_ENCODING_LINEAR_16 + ) + ), + event_input=session.EventInput(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].participant == "participant_value" + + assert args[0].event_input == session.EventInput(name="name_value") + + +@pytest.mark.asyncio +async def test_analyze_content_flattened_error_async(): + client = ParticipantsAsyncClient(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.analyze_content( + gcd_participant.AnalyzeContentRequest(), + participant="participant_value", + text_input=session.TextInput(text="text_value"), + audio_input=gcd_participant.AudioInput( + config=audio_config.InputAudioConfig( + audio_encoding=audio_config.AudioEncoding.AUDIO_ENCODING_LINEAR_16 + ) + ), + event_input=session.EventInput(name="name_value"), + ) + + +def test_streaming_analyze_content( + transport: str = "grpc", request_type=participant.StreamingAnalyzeContentRequest +): + client = ParticipantsClient( + 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_analyze_content), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = iter([participant.StreamingAnalyzeContentResponse()]) + + response = client.streaming_analyze_content(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, participant.StreamingAnalyzeContentResponse) + + +def test_streaming_analyze_content_from_dict(): + test_streaming_analyze_content(request_type=dict) + + +@pytest.mark.asyncio +async def test_streaming_analyze_content_async( + transport: str = "grpc_asyncio", + request_type=participant.StreamingAnalyzeContentRequest, +): + client = ParticipantsAsyncClient( + 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_analyze_content), "__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=[participant.StreamingAnalyzeContentResponse()] + ) + + response = await client.streaming_analyze_content(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, participant.StreamingAnalyzeContentResponse) + + +@pytest.mark.asyncio +async def test_streaming_analyze_content_async_from_dict(): + await test_streaming_analyze_content_async(request_type=dict) + + +def test_suggest_articles( + transport: str = "grpc", request_type=participant.SuggestArticlesRequest +): + client = ParticipantsClient( + 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.suggest_articles), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestArticlesResponse( + latest_message="latest_message_value", context_size=1311, + ) + + response = client.suggest_articles(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestArticlesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, participant.SuggestArticlesResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +def test_suggest_articles_from_dict(): + test_suggest_articles(request_type=dict) + + +def test_suggest_articles_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.suggest_articles), "__call__") as call: + client.suggest_articles() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestArticlesRequest() + + +@pytest.mark.asyncio +async def test_suggest_articles_async( + transport: str = "grpc_asyncio", request_type=participant.SuggestArticlesRequest +): + client = ParticipantsAsyncClient( + 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.suggest_articles), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestArticlesResponse( + latest_message="latest_message_value", context_size=1311, + ) + ) + + response = await client.suggest_articles(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestArticlesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, participant.SuggestArticlesResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +@pytest.mark.asyncio +async def test_suggest_articles_async_from_dict(): + await test_suggest_articles_async(request_type=dict) + + +def test_suggest_articles_field_headers(): + client = ParticipantsClient(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 = participant.SuggestArticlesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.suggest_articles), "__call__") as call: + call.return_value = participant.SuggestArticlesResponse() + + client.suggest_articles(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_suggest_articles_field_headers_async(): + client = ParticipantsAsyncClient(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 = participant.SuggestArticlesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.suggest_articles), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestArticlesResponse() + ) + + await client.suggest_articles(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_suggest_articles_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.suggest_articles), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestArticlesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.suggest_articles(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_suggest_articles_flattened_error(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.suggest_articles( + participant.SuggestArticlesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_suggest_articles_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.suggest_articles), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestArticlesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestArticlesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.suggest_articles(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_suggest_articles_flattened_error_async(): + client = ParticipantsAsyncClient(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.suggest_articles( + participant.SuggestArticlesRequest(), parent="parent_value", + ) + + +def test_suggest_faq_answers( + transport: str = "grpc", request_type=participant.SuggestFaqAnswersRequest +): + client = ParticipantsClient( + 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.suggest_faq_answers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestFaqAnswersResponse( + latest_message="latest_message_value", context_size=1311, + ) + + response = client.suggest_faq_answers(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestFaqAnswersRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, participant.SuggestFaqAnswersResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +def test_suggest_faq_answers_from_dict(): + test_suggest_faq_answers(request_type=dict) + + +def test_suggest_faq_answers_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_faq_answers), "__call__" + ) as call: + client.suggest_faq_answers() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestFaqAnswersRequest() + + +@pytest.mark.asyncio +async def test_suggest_faq_answers_async( + transport: str = "grpc_asyncio", request_type=participant.SuggestFaqAnswersRequest +): + client = ParticipantsAsyncClient( + 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.suggest_faq_answers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestFaqAnswersResponse( + latest_message="latest_message_value", context_size=1311, + ) + ) + + response = await client.suggest_faq_answers(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestFaqAnswersRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, participant.SuggestFaqAnswersResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +@pytest.mark.asyncio +async def test_suggest_faq_answers_async_from_dict(): + await test_suggest_faq_answers_async(request_type=dict) + + +def test_suggest_faq_answers_field_headers(): + client = ParticipantsClient(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 = participant.SuggestFaqAnswersRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_faq_answers), "__call__" + ) as call: + call.return_value = participant.SuggestFaqAnswersResponse() + + client.suggest_faq_answers(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_suggest_faq_answers_field_headers_async(): + client = ParticipantsAsyncClient(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 = participant.SuggestFaqAnswersRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_faq_answers), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestFaqAnswersResponse() + ) + + await client.suggest_faq_answers(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_suggest_faq_answers_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_faq_answers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestFaqAnswersResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.suggest_faq_answers(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_suggest_faq_answers_flattened_error(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.suggest_faq_answers( + participant.SuggestFaqAnswersRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_suggest_faq_answers_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_faq_answers), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestFaqAnswersResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestFaqAnswersResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.suggest_faq_answers(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_suggest_faq_answers_flattened_error_async(): + client = ParticipantsAsyncClient(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.suggest_faq_answers( + participant.SuggestFaqAnswersRequest(), parent="parent_value", + ) + + +def test_suggest_smart_replies( + transport: str = "grpc", request_type=participant.SuggestSmartRepliesRequest +): + client = ParticipantsClient( + 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.suggest_smart_replies), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestSmartRepliesResponse( + latest_message="latest_message_value", context_size=1311, + ) + + response = client.suggest_smart_replies(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestSmartRepliesRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, participant.SuggestSmartRepliesResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +def test_suggest_smart_replies_from_dict(): + test_suggest_smart_replies(request_type=dict) + + +def test_suggest_smart_replies_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_smart_replies), "__call__" + ) as call: + client.suggest_smart_replies() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestSmartRepliesRequest() + + +@pytest.mark.asyncio +async def test_suggest_smart_replies_async( + transport: str = "grpc_asyncio", request_type=participant.SuggestSmartRepliesRequest +): + client = ParticipantsAsyncClient( + 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.suggest_smart_replies), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestSmartRepliesResponse( + latest_message="latest_message_value", context_size=1311, + ) + ) + + response = await client.suggest_smart_replies(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.SuggestSmartRepliesRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, participant.SuggestSmartRepliesResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +@pytest.mark.asyncio +async def test_suggest_smart_replies_async_from_dict(): + await test_suggest_smart_replies_async(request_type=dict) + + +def test_suggest_smart_replies_field_headers(): + client = ParticipantsClient(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 = participant.SuggestSmartRepliesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_smart_replies), "__call__" + ) as call: + call.return_value = participant.SuggestSmartRepliesResponse() + + client.suggest_smart_replies(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_suggest_smart_replies_field_headers_async(): + client = ParticipantsAsyncClient(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 = participant.SuggestSmartRepliesRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_smart_replies), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestSmartRepliesResponse() + ) + + await client.suggest_smart_replies(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_suggest_smart_replies_flattened(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_smart_replies), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestSmartRepliesResponse() + + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.suggest_smart_replies(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_suggest_smart_replies_flattened_error(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.suggest_smart_replies( + participant.SuggestSmartRepliesRequest(), parent="parent_value", + ) + + +@pytest.mark.asyncio +async def test_suggest_smart_replies_flattened_async(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials(),) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.suggest_smart_replies), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.SuggestSmartRepliesResponse() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.SuggestSmartRepliesResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.suggest_smart_replies(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_suggest_smart_replies_flattened_error_async(): + client = ParticipantsAsyncClient(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.suggest_smart_replies( + participant.SuggestSmartRepliesRequest(), parent="parent_value", + ) + + +def test_list_suggestions( + transport: str = "grpc", request_type=participant.ListSuggestionsRequest +): + client = ParticipantsClient( + 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_suggestions), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = participant.ListSuggestionsResponse( + next_page_token="next_page_token_value", + ) + + response = client.list_suggestions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.ListSuggestionsRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListSuggestionsPager) + + assert response.next_page_token == "next_page_token_value" + + +def test_list_suggestions_from_dict(): + test_list_suggestions(request_type=dict) + + +def test_list_suggestions_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_suggestions), "__call__") as call: + client.list_suggestions() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.ListSuggestionsRequest() + + +@pytest.mark.asyncio +async def test_list_suggestions_async( + transport: str = "grpc_asyncio", request_type=participant.ListSuggestionsRequest +): + client = ParticipantsAsyncClient( + 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_suggestions), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.ListSuggestionsResponse( + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_suggestions(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.ListSuggestionsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListSuggestionsAsyncPager) + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_suggestions_async_from_dict(): + await test_list_suggestions_async(request_type=dict) + + +def test_list_suggestions_field_headers(): + client = ParticipantsClient(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 = participant.ListSuggestionsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_suggestions), "__call__") as call: + call.return_value = participant.ListSuggestionsResponse() + + client.list_suggestions(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_suggestions_field_headers_async(): + client = ParticipantsAsyncClient(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 = participant.ListSuggestionsRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_suggestions), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.ListSuggestionsResponse() + ) + + await client.list_suggestions(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_suggestions_pager(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_suggestions), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListSuggestionsResponse( + suggestions=[ + participant.Suggestion(), + participant.Suggestion(), + participant.Suggestion(), + ], + next_page_token="abc", + ), + participant.ListSuggestionsResponse(suggestions=[], next_page_token="def",), + participant.ListSuggestionsResponse( + suggestions=[participant.Suggestion(),], next_page_token="ghi", + ), + participant.ListSuggestionsResponse( + suggestions=[participant.Suggestion(), participant.Suggestion(),], + ), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("parent", ""),)), + ) + pager = client.list_suggestions(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, participant.Suggestion) for i in results) + + +def test_list_suggestions_pages(): + client = ParticipantsClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_suggestions), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListSuggestionsResponse( + suggestions=[ + participant.Suggestion(), + participant.Suggestion(), + participant.Suggestion(), + ], + next_page_token="abc", + ), + participant.ListSuggestionsResponse(suggestions=[], next_page_token="def",), + participant.ListSuggestionsResponse( + suggestions=[participant.Suggestion(),], next_page_token="ghi", + ), + participant.ListSuggestionsResponse( + suggestions=[participant.Suggestion(), participant.Suggestion(),], + ), + RuntimeError, + ) + pages = list(client.list_suggestions(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_suggestions_async_pager(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_suggestions), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListSuggestionsResponse( + suggestions=[ + participant.Suggestion(), + participant.Suggestion(), + participant.Suggestion(), + ], + next_page_token="abc", + ), + participant.ListSuggestionsResponse(suggestions=[], next_page_token="def",), + participant.ListSuggestionsResponse( + suggestions=[participant.Suggestion(),], next_page_token="ghi", + ), + participant.ListSuggestionsResponse( + suggestions=[participant.Suggestion(), participant.Suggestion(),], + ), + RuntimeError, + ) + async_pager = await client.list_suggestions(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, participant.Suggestion) for i in responses) + + +@pytest.mark.asyncio +async def test_list_suggestions_async_pages(): + client = ParticipantsAsyncClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_suggestions), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + participant.ListSuggestionsResponse( + suggestions=[ + participant.Suggestion(), + participant.Suggestion(), + participant.Suggestion(), + ], + next_page_token="abc", + ), + participant.ListSuggestionsResponse(suggestions=[], next_page_token="def",), + participant.ListSuggestionsResponse( + suggestions=[participant.Suggestion(),], next_page_token="ghi", + ), + participant.ListSuggestionsResponse( + suggestions=[participant.Suggestion(), participant.Suggestion(),], + ), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_suggestions(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +def test_compile_suggestion( + transport: str = "grpc", request_type=participant.CompileSuggestionRequest +): + client = ParticipantsClient( + 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.compile_suggestion), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = participant.CompileSuggestionResponse( + latest_message="latest_message_value", context_size=1311, + ) + + response = client.compile_suggestion(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.CompileSuggestionRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, participant.CompileSuggestionResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +def test_compile_suggestion_from_dict(): + test_compile_suggestion(request_type=dict) + + +def test_compile_suggestion_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.compile_suggestion), "__call__" + ) as call: + client.compile_suggestion() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.CompileSuggestionRequest() + + +@pytest.mark.asyncio +async def test_compile_suggestion_async( + transport: str = "grpc_asyncio", request_type=participant.CompileSuggestionRequest +): + client = ParticipantsAsyncClient( + 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.compile_suggestion), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.CompileSuggestionResponse( + latest_message="latest_message_value", context_size=1311, + ) + ) + + response = await client.compile_suggestion(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == participant.CompileSuggestionRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, participant.CompileSuggestionResponse) + + assert response.latest_message == "latest_message_value" + + assert response.context_size == 1311 + + +@pytest.mark.asyncio +async def test_compile_suggestion_async_from_dict(): + await test_compile_suggestion_async(request_type=dict) + + +def test_compile_suggestion_field_headers(): + client = ParticipantsClient(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 = participant.CompileSuggestionRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.compile_suggestion), "__call__" + ) as call: + call.return_value = participant.CompileSuggestionResponse() + + client.compile_suggestion(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_compile_suggestion_field_headers_async(): + client = ParticipantsAsyncClient(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 = participant.CompileSuggestionRequest() + request.parent = "parent/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.compile_suggestion), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + participant.CompileSuggestionResponse() + ) + + await client.compile_suggestion(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_credentials_transport_error(): + # It is an error to provide credentials and a transport instance. + transport = transports.ParticipantsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), transport=transport, + ) + + # It is an error to provide a credentials file and a transport instance. + transport = transports.ParticipantsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ParticipantsClient( + client_options={"credentials_file": "credentials.json"}, + transport=transport, + ) + + # It is an error to provide scopes and a transport instance. + transport = transports.ParticipantsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + with pytest.raises(ValueError): + client = ParticipantsClient( + client_options={"scopes": ["1", "2"]}, transport=transport, + ) + + +def test_transport_instance(): + # A client may be instantiated with a custom transport instance. + transport = transports.ParticipantsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + client = ParticipantsClient(transport=transport) + assert client.transport is transport + + +def test_transport_get_channel(): + # A client may be instantiated with a custom transport instance. + transport = transports.ParticipantsGrpcTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + transport = transports.ParticipantsGrpcAsyncIOTransport( + credentials=credentials.AnonymousCredentials(), + ) + channel = transport.grpc_channel + assert channel + + +@pytest.mark.parametrize( + "transport_class", + [ + transports.ParticipantsGrpcTransport, + transports.ParticipantsGrpcAsyncIOTransport, + ], +) +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 = ParticipantsClient(credentials=credentials.AnonymousCredentials(),) + assert isinstance(client.transport, transports.ParticipantsGrpcTransport,) + + +def test_participants_base_transport_error(): + # Passing both a credentials object and credentials_file should raise an error + with pytest.raises(exceptions.DuplicateCredentialArgs): + transport = transports.ParticipantsTransport( + credentials=credentials.AnonymousCredentials(), + credentials_file="credentials.json", + ) + + +def test_participants_base_transport(): + # Instantiate the base transport. + with mock.patch( + "google.cloud.dialogflow_v2beta1.services.participants.transports.ParticipantsTransport.__init__" + ) as Transport: + Transport.return_value = None + transport = transports.ParticipantsTransport( + credentials=credentials.AnonymousCredentials(), + ) + + # Every method on the transport should just blindly + # raise NotImplementedError. + methods = ( + "create_participant", + "get_participant", + "list_participants", + "update_participant", + "analyze_content", + "streaming_analyze_content", + "suggest_articles", + "suggest_faq_answers", + "suggest_smart_replies", + "list_suggestions", + "compile_suggestion", + ) + for method in methods: + with pytest.raises(NotImplementedError): + getattr(transport, method)(request=object()) + + +def test_participants_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.dialogflow_v2beta1.services.participants.transports.ParticipantsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + load_creds.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ParticipantsTransport( + 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_participants_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.dialogflow_v2beta1.services.participants.transports.ParticipantsTransport._prep_wrapped_messages" + ) as Transport: + Transport.return_value = None + adc.return_value = (credentials.AnonymousCredentials(), None) + transport = transports.ParticipantsTransport() + adc.assert_called_once() + + +def test_participants_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) + ParticipantsClient() + 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_participants_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.ParticipantsGrpcTransport( + 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", + ) + + +@pytest.mark.parametrize( + "transport_class", + [transports.ParticipantsGrpcTransport, transports.ParticipantsGrpcAsyncIOTransport], +) +def test_participants_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + +def test_participants_host_no_port(): + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:443" + + +def test_participants_host_with_port(): + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), + client_options=client_options.ClientOptions( + api_endpoint="dialogflow.googleapis.com:8000" + ), + ) + assert client.transport._host == "dialogflow.googleapis.com:8000" + + +def test_participants_grpc_transport_channel(): + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ParticipantsGrpcTransport( + 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_participants_grpc_asyncio_transport_channel(): + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) + + # Check that channel is used if provided. + transport = transports.ParticipantsGrpcAsyncIOTransport( + host="squid.clam.whelk", channel=channel, + ) + assert transport.grpc_channel == channel + assert transport._host == "squid.clam.whelk:443" + assert transport._ssl_channel_credentials == None + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.ParticipantsGrpcTransport, transports.ParticipantsGrpcAsyncIOTransport], +) +def test_participants_transport_channel_mtls_with_client_cert_source(transport_class): + with mock.patch( + "grpc.ssl_channel_credentials", autospec=True + ) as grpc_ssl_channel_cred: + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_ssl_cred = mock.Mock() + grpc_ssl_channel_cred.return_value = mock_ssl_cred + + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + + cred = credentials.AnonymousCredentials() + with pytest.warns(DeprecationWarning): + with mock.patch.object(auth, "default") as adc: + adc.return_value = (cred, None) + transport = transport_class( + host="squid.clam.whelk", + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=client_cert_source_callback, + ) + adc.assert_called_once() + + grpc_ssl_channel_cred.assert_called_once_with( + certificate_chain=b"cert bytes", private_key=b"key bytes" + ) + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + assert transport._ssl_channel_credentials == mock_ssl_cred + + +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. +@pytest.mark.parametrize( + "transport_class", + [transports.ParticipantsGrpcTransport, transports.ParticipantsGrpcAsyncIOTransport], +) +def test_participants_transport_channel_mtls_with_adc(transport_class): + mock_ssl_cred = mock.Mock() + with mock.patch.multiple( + "google.auth.transport.grpc.SslCredentials", + __init__=mock.Mock(return_value=None), + ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), + ): + with mock.patch.object( + transport_class, "create_channel" + ) as grpc_create_channel: + mock_grpc_channel = mock.Mock() + grpc_create_channel.return_value = mock_grpc_channel + mock_cred = mock.Mock() + + with pytest.warns(DeprecationWarning): + transport = transport_class( + host="squid.clam.whelk", + credentials=mock_cred, + api_mtls_endpoint="mtls.squid.clam.whelk", + client_cert_source=None, + ) + + grpc_create_channel.assert_called_once_with( + "mtls.squid.clam.whelk:443", + credentials=mock_cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_cred, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + assert transport.grpc_channel == mock_grpc_channel + + +def test_context_path(): + project = "squid" + session = "clam" + context = "whelk" + + expected = "projects/{project}/agent/sessions/{session}/contexts/{context}".format( + project=project, session=session, context=context, + ) + actual = ParticipantsClient.context_path(project, session, context) + assert expected == actual + + +def test_parse_context_path(): + expected = { + "project": "octopus", + "session": "oyster", + "context": "nudibranch", + } + path = ParticipantsClient.context_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_context_path(path) + assert expected == actual + + +def test_document_path(): + project = "cuttlefish" + knowledge_base = "mussel" + document = "winkle" + + expected = "projects/{project}/knowledgeBases/{knowledge_base}/documents/{document}".format( + project=project, knowledge_base=knowledge_base, document=document, + ) + actual = ParticipantsClient.document_path(project, knowledge_base, document) + assert expected == actual + + +def test_parse_document_path(): + expected = { + "project": "nautilus", + "knowledge_base": "scallop", + "document": "abalone", + } + path = ParticipantsClient.document_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_document_path(path) + assert expected == actual + + +def test_intent_path(): + project = "squid" + intent = "clam" + + expected = "projects/{project}/agent/intents/{intent}".format( + project=project, intent=intent, + ) + actual = ParticipantsClient.intent_path(project, intent) + assert expected == actual + + +def test_parse_intent_path(): + expected = { + "project": "whelk", + "intent": "octopus", + } + path = ParticipantsClient.intent_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_intent_path(path) + assert expected == actual + + +def test_message_path(): + project = "oyster" + conversation = "nudibranch" + message = "cuttlefish" + + expected = "projects/{project}/conversations/{conversation}/messages/{message}".format( + project=project, conversation=conversation, message=message, + ) + actual = ParticipantsClient.message_path(project, conversation, message) + assert expected == actual + + +def test_parse_message_path(): + expected = { + "project": "mussel", + "conversation": "winkle", + "message": "nautilus", + } + path = ParticipantsClient.message_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_message_path(path) + assert expected == actual + + +def test_participant_path(): + project = "scallop" + conversation = "abalone" + participant = "squid" + + expected = "projects/{project}/conversations/{conversation}/participants/{participant}".format( + project=project, conversation=conversation, participant=participant, + ) + actual = ParticipantsClient.participant_path(project, conversation, participant) + assert expected == actual + + +def test_parse_participant_path(): + expected = { + "project": "clam", + "conversation": "whelk", + "participant": "octopus", + } + path = ParticipantsClient.participant_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_participant_path(path) + assert expected == actual + + +def test_session_entity_type_path(): + project = "oyster" + session = "nudibranch" + entity_type = "cuttlefish" + + expected = "projects/{project}/agent/sessions/{session}/entityTypes/{entity_type}".format( + project=project, session=session, entity_type=entity_type, + ) + actual = ParticipantsClient.session_entity_type_path(project, session, entity_type) + assert expected == actual + + +def test_parse_session_entity_type_path(): + expected = { + "project": "mussel", + "session": "winkle", + "entity_type": "nautilus", + } + path = ParticipantsClient.session_entity_type_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.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 = ParticipantsClient.common_billing_account_path(billing_account) + assert expected == actual + + +def test_parse_common_billing_account_path(): + expected = { + "billing_account": "abalone", + } + path = ParticipantsClient.common_billing_account_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_common_billing_account_path(path) + assert expected == actual + + +def test_common_folder_path(): + folder = "squid" + + expected = "folders/{folder}".format(folder=folder,) + actual = ParticipantsClient.common_folder_path(folder) + assert expected == actual + + +def test_parse_common_folder_path(): + expected = { + "folder": "clam", + } + path = ParticipantsClient.common_folder_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_common_folder_path(path) + assert expected == actual + + +def test_common_organization_path(): + organization = "whelk" + + expected = "organizations/{organization}".format(organization=organization,) + actual = ParticipantsClient.common_organization_path(organization) + assert expected == actual + + +def test_parse_common_organization_path(): + expected = { + "organization": "octopus", + } + path = ParticipantsClient.common_organization_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.parse_common_organization_path(path) + assert expected == actual + + +def test_common_project_path(): + project = "oyster" + + expected = "projects/{project}".format(project=project,) + actual = ParticipantsClient.common_project_path(project) + assert expected == actual + + +def test_parse_common_project_path(): + expected = { + "project": "nudibranch", + } + path = ParticipantsClient.common_project_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.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 = ParticipantsClient.common_location_path(project, location) + assert expected == actual + + +def test_parse_common_location_path(): + expected = { + "project": "winkle", + "location": "nautilus", + } + path = ParticipantsClient.common_location_path(**expected) + + # Check that the path construction is reversible. + actual = ParticipantsClient.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.ParticipantsTransport, "_prep_wrapped_messages" + ) as prep: + client = ParticipantsClient( + credentials=credentials.AnonymousCredentials(), client_info=client_info, + ) + prep.assert_called_once_with(client_info) + + with mock.patch.object( + transports.ParticipantsTransport, "_prep_wrapped_messages" + ) as prep: + transport_class = ParticipantsClient.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/dialogflow_v2beta1/test_session_entity_types.py b/tests/unit/gapic/dialogflow_v2beta1/test_session_entity_types.py index 8745a8e1c..62894918b 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/test_session_entity_types.py +++ b/tests/unit/gapic/dialogflow_v2beta1/test_session_entity_types.py @@ -95,7 +95,24 @@ def test__get_default_mtls_endpoint(): @pytest.mark.parametrize( - "client_class", [SessionEntityTypesClient, SessionEntityTypesAsyncClient] + "client_class", [SessionEntityTypesClient, SessionEntityTypesAsyncClient,] +) +def test_session_entity_types_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize( + "client_class", [SessionEntityTypesClient, SessionEntityTypesAsyncClient,] ) def test_session_entity_types_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() @@ -105,16 +122,21 @@ def test_session_entity_types_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_session_entity_types_client_get_transport_class(): transport = SessionEntityTypesClient.get_transport_class() - assert transport == transports.SessionEntityTypesGrpcTransport + available_transports = [ + transports.SessionEntityTypesGrpcTransport, + ] + assert transport in available_transports transport = SessionEntityTypesClient.get_transport_class("grpc") assert transport == transports.SessionEntityTypesGrpcTransport @@ -165,7 +187,7 @@ def test_session_entity_types_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -181,7 +203,7 @@ def test_session_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -197,7 +219,7 @@ def test_session_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -225,7 +247,7 @@ def test_session_entity_types_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -286,29 +308,25 @@ def test_session_entity_types_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -317,66 +335,53 @@ def test_session_entity_types_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -402,7 +407,7 @@ def test_session_entity_types_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -432,7 +437,7 @@ def test_session_entity_types_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -451,7 +456,7 @@ def test_session_entity_types_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -497,6 +502,24 @@ def test_list_session_entity_types_from_dict(): test_list_session_entity_types(request_type=dict) +def test_list_session_entity_types_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_session_entity_types), "__call__" + ) as call: + client.list_session_entity_types() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.ListSessionEntityTypesRequest() + + @pytest.mark.asyncio async def test_list_session_entity_types_async( transport: str = "grpc_asyncio", @@ -896,6 +919,24 @@ def test_get_session_entity_type_from_dict(): test_get_session_entity_type(request_type=dict) +def test_get_session_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_session_entity_type), "__call__" + ) as call: + client.get_session_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.GetSessionEntityTypeRequest() + + @pytest.mark.asyncio async def test_get_session_entity_type_async( transport: str = "grpc_asyncio", @@ -1123,6 +1164,24 @@ def test_create_session_entity_type_from_dict(): test_create_session_entity_type(request_type=dict) +def test_create_session_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.create_session_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_session_entity_type.CreateSessionEntityTypeRequest() + + @pytest.mark.asyncio async def test_create_session_entity_type_async( transport: str = "grpc_asyncio", @@ -1376,6 +1435,24 @@ def test_update_session_entity_type_from_dict(): test_update_session_entity_type(request_type=dict) +def test_update_session_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.update_session_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_session_entity_type.UpdateSessionEntityTypeRequest() + + @pytest.mark.asyncio async def test_update_session_entity_type_async( transport: str = "grpc_asyncio", @@ -1624,6 +1701,24 @@ def test_delete_session_entity_type_from_dict(): test_delete_session_entity_type(request_type=dict) +def test_delete_session_entity_type_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionEntityTypesClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # 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: + client.delete_session_entity_type() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == session_entity_type.DeleteSessionEntityTypeRequest() + + @pytest.mark.asyncio async def test_delete_session_entity_type_async( transport: str = "grpc_asyncio", @@ -1961,6 +2056,56 @@ def test_session_entity_types_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.SessionEntityTypesGrpcTransport, + transports.SessionEntityTypesGrpcAsyncIOTransport, + ], +) +def test_session_entity_types_grpc_transport_client_cert_source_for_mtls( + transport_class, +): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_session_entity_types_host_no_port(): client = SessionEntityTypesClient( credentials=credentials.AnonymousCredentials(), @@ -1982,7 +2127,7 @@ def test_session_entity_types_host_with_port(): def test_session_entity_types_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SessionEntityTypesGrpcTransport( @@ -1994,7 +2139,7 @@ def test_session_entity_types_grpc_transport_channel(): def test_session_entity_types_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SessionEntityTypesGrpcAsyncIOTransport( @@ -2005,6 +2150,8 @@ def test_session_entity_types_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [ @@ -2019,7 +2166,7 @@ def test_session_entity_types_transport_channel_mtls_with_client_cert_source( "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -2060,6 +2207,8 @@ def test_session_entity_types_transport_channel_mtls_with_client_cert_source( assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [ @@ -2075,7 +2224,7 @@ def test_session_entity_types_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel diff --git a/tests/unit/gapic/dialogflow_v2beta1/test_sessions.py b/tests/unit/gapic/dialogflow_v2beta1/test_sessions.py index f1a0df490..81d572606 100644 --- a/tests/unit/gapic/dialogflow_v2beta1/test_sessions.py +++ b/tests/unit/gapic/dialogflow_v2beta1/test_sessions.py @@ -88,7 +88,22 @@ def test__get_default_mtls_endpoint(): assert SessionsClient._get_default_mtls_endpoint(non_googleapi) == non_googleapi -@pytest.mark.parametrize("client_class", [SessionsClient, SessionsAsyncClient]) +@pytest.mark.parametrize("client_class", [SessionsClient, SessionsAsyncClient,]) +def test_sessions_client_from_service_account_info(client_class): + creds = credentials.AnonymousCredentials() + with mock.patch.object( + service_account.Credentials, "from_service_account_info" + ) as factory: + factory.return_value = creds + info = {"valid": True} + client = client_class.from_service_account_info(info) + assert client.transport._credentials == creds + assert isinstance(client, client_class) + + assert client.transport._host == "dialogflow.googleapis.com:443" + + +@pytest.mark.parametrize("client_class", [SessionsClient, SessionsAsyncClient,]) def test_sessions_client_from_service_account_file(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( @@ -97,16 +112,21 @@ def test_sessions_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "dialogflow.googleapis.com:443" def test_sessions_client_get_transport_class(): transport = SessionsClient.get_transport_class() - assert transport == transports.SessionsGrpcTransport + available_transports = [ + transports.SessionsGrpcTransport, + ] + assert transport in available_transports transport = SessionsClient.get_transport_class("grpc") assert transport == transports.SessionsGrpcTransport @@ -149,7 +169,7 @@ def test_sessions_client_client_options(client_class, transport_class, transport credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -165,7 +185,7 @@ def test_sessions_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -181,7 +201,7 @@ def test_sessions_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -209,7 +229,7 @@ def test_sessions_client_client_options(client_class, transport_class, transport credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -258,29 +278,25 @@ def test_sessions_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -289,66 +305,53 @@ def test_sessions_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -370,7 +373,7 @@ def test_sessions_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -396,7 +399,7 @@ def test_sessions_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -413,7 +416,7 @@ def test_sessions_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -458,6 +461,22 @@ def test_detect_intent_from_dict(): test_detect_intent(request_type=dict) +def test_detect_intent_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SessionsClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.detect_intent), "__call__") as call: + client.detect_intent() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == gcd_session.DetectIntentRequest() + + @pytest.mark.asyncio async def test_detect_intent_async( transport: str = "grpc_asyncio", request_type=gcd_session.DetectIntentRequest @@ -791,7 +810,7 @@ def test_transport_get_channel(): @pytest.mark.parametrize( "transport_class", - [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], + [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport,], ) def test_transport_adc(transport_class): # Test default credentials are used if not provided. @@ -901,6 +920,51 @@ def test_sessions_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], +) +def test_sessions_grpc_transport_client_cert_source_for_mtls(transport_class): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=( + "https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/dialogflow", + ), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_sessions_host_no_port(): client = SessionsClient( credentials=credentials.AnonymousCredentials(), @@ -922,7 +986,7 @@ def test_sessions_host_with_port(): def test_sessions_grpc_transport_channel(): - channel = grpc.insecure_channel("http://localhost/") + channel = grpc.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SessionsGrpcTransport( @@ -934,7 +998,7 @@ def test_sessions_grpc_transport_channel(): def test_sessions_grpc_asyncio_transport_channel(): - channel = aio.insecure_channel("http://localhost/") + channel = aio.secure_channel("http://localhost/", grpc.local_channel_credentials()) # Check that channel is used if provided. transport = transports.SessionsGrpcAsyncIOTransport( @@ -945,6 +1009,8 @@ def test_sessions_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], @@ -954,7 +1020,7 @@ def test_sessions_transport_channel_mtls_with_client_cert_source(transport_class "grpc.ssl_channel_credentials", autospec=True ) as grpc_ssl_channel_cred: with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_ssl_cred = mock.Mock() grpc_ssl_channel_cred.return_value = mock_ssl_cred @@ -995,6 +1061,8 @@ def test_sessions_transport_channel_mtls_with_client_cert_source(transport_class assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [transports.SessionsGrpcTransport, transports.SessionsGrpcAsyncIOTransport], @@ -1007,7 +1075,7 @@ def test_sessions_transport_channel_mtls_with_adc(transport_class): ssl_credentials=mock.PropertyMock(return_value=mock_ssl_cred), ): with mock.patch.object( - transport_class, "create_channel", autospec=True + transport_class, "create_channel" ) as grpc_create_channel: mock_grpc_channel = mock.Mock() grpc_create_channel.return_value = mock_grpc_channel