diff --git a/contactcenterinsights-v1-py.tar.gz b/contactcenterinsights-v1-py.tar.gz new file mode 100644 index 0000000..e69de29 diff --git a/google/cloud/contact_center_insights/__init__.py b/google/cloud/contact_center_insights/__init__.py index bd481dc..68bc691 100644 --- a/google/cloud/contact_center_insights/__init__.py +++ b/google/cloud/contact_center_insights/__init__.py @@ -84,6 +84,8 @@ UpdatePhraseMatcherRequest, UpdateSettingsRequest, UpdateViewRequest, + UploadConversationMetadata, + UploadConversationRequest, ) from google.cloud.contact_center_insights_v1.types.resources import ( Analysis, @@ -120,6 +122,7 @@ PhraseMatchRule, PhraseMatchRuleConfig, PhraseMatchRuleGroup, + RedactionConfig, RuntimeAnnotation, SentimentData, Settings, @@ -190,6 +193,8 @@ "UpdatePhraseMatcherRequest", "UpdateSettingsRequest", "UpdateViewRequest", + "UploadConversationMetadata", + "UploadConversationRequest", "ConversationView", "Analysis", "AnalysisResult", @@ -225,6 +230,7 @@ "PhraseMatchRule", "PhraseMatchRuleConfig", "PhraseMatchRuleGroup", + "RedactionConfig", "RuntimeAnnotation", "SentimentData", "Settings", diff --git a/google/cloud/contact_center_insights_v1/__init__.py b/google/cloud/contact_center_insights_v1/__init__.py index 1be00ae..edc8d4a 100644 --- a/google/cloud/contact_center_insights_v1/__init__.py +++ b/google/cloud/contact_center_insights_v1/__init__.py @@ -82,6 +82,8 @@ UpdatePhraseMatcherRequest, UpdateSettingsRequest, UpdateViewRequest, + UploadConversationMetadata, + UploadConversationRequest, ) from .types.resources import ( Analysis, @@ -118,6 +120,7 @@ PhraseMatchRule, PhraseMatchRuleConfig, PhraseMatchRuleGroup, + RedactionConfig, RuntimeAnnotation, SentimentData, Settings, @@ -214,6 +217,7 @@ "PhraseMatchRuleConfig", "PhraseMatchRuleGroup", "PhraseMatcher", + "RedactionConfig", "RuntimeAnnotation", "SentimentData", "Settings", @@ -229,5 +233,7 @@ "UpdatePhraseMatcherRequest", "UpdateSettingsRequest", "UpdateViewRequest", + "UploadConversationMetadata", + "UploadConversationRequest", "View", ) diff --git a/google/cloud/contact_center_insights_v1/gapic_metadata.json b/google/cloud/contact_center_insights_v1/gapic_metadata.json index 6e65f45..bc4e058 100644 --- a/google/cloud/contact_center_insights_v1/gapic_metadata.json +++ b/google/cloud/contact_center_insights_v1/gapic_metadata.json @@ -194,6 +194,11 @@ "methods": [ "update_view" ] + }, + "UploadConversation": { + "methods": [ + "upload_conversation" + ] } } }, @@ -384,6 +389,11 @@ "methods": [ "update_view" ] + }, + "UploadConversation": { + "methods": [ + "upload_conversation" + ] } } }, @@ -574,6 +584,11 @@ "methods": [ "update_view" ] + }, + "UploadConversation": { + "methods": [ + "upload_conversation" + ] } } } diff --git a/google/cloud/contact_center_insights_v1/services/contact_center_insights/async_client.py b/google/cloud/contact_center_insights_v1/services/contact_center_insights/async_client.py index a70fd5e..52c1f19 100644 --- a/google/cloud/contact_center_insights_v1/services/contact_center_insights/async_client.py +++ b/google/cloud/contact_center_insights_v1/services/contact_center_insights/async_client.py @@ -381,6 +381,104 @@ async def sample_create_conversation(): # Done; return the response. return response + async def upload_conversation( + self, + request: Optional[ + Union[contact_center_insights.UploadConversationRequest, dict] + ] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation_async.AsyncOperation: + r"""Create a longrunning conversation upload operation. + This method differs from CreateConversation by allowing + audio transcription and optional DLP redaction. + + .. code-block:: python + + # This snippet has been automatically generated and should be regarded as a + # code template only. + # It will require modifications to work: + # - It may require correct/in-range values for request initialization. + # - It may require specifying regional endpoints when creating the service + # client as shown in: + # https://googleapis.dev/python/google-api-core/latest/client_options.html + from google.cloud import contact_center_insights_v1 + + async def sample_upload_conversation(): + # Create a client + client = contact_center_insights_v1.ContactCenterInsightsAsyncClient() + + # Initialize request argument(s) + request = contact_center_insights_v1.UploadConversationRequest( + parent="parent_value", + ) + + # Make the request + operation = client.upload_conversation(request=request) + + print("Waiting for operation to complete...") + + response = (await operation).result() + + # Handle the response + print(response) + + Args: + request (Optional[Union[google.cloud.contact_center_insights_v1.types.UploadConversationRequest, dict]]): + The request object. Request to upload a conversation. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + 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.contact_center_insights_v1.types.Conversation` + The conversation resource. + + """ + # Create or coerce a protobuf request object. + request = contact_center_insights.UploadConversationRequest(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.upload_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, + ) + + # Wrap the response in an operation future. + response = operation_async.from_gapic( + response, + self._client._transport.operations_client, + resources.Conversation, + metadata_type=contact_center_insights.UploadConversationMetadata, + ) + + # Done; return the response. + return response + async def update_conversation( self, request: Optional[ diff --git a/google/cloud/contact_center_insights_v1/services/contact_center_insights/client.py b/google/cloud/contact_center_insights_v1/services/contact_center_insights/client.py index 11346c4..0c31103 100644 --- a/google/cloud/contact_center_insights_v1/services/contact_center_insights/client.py +++ b/google/cloud/contact_center_insights_v1/services/contact_center_insights/client.py @@ -742,6 +742,105 @@ def sample_create_conversation(): # Done; return the response. return response + def upload_conversation( + self, + request: Optional[ + Union[contact_center_insights.UploadConversationRequest, dict] + ] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operation.Operation: + r"""Create a longrunning conversation upload operation. + This method differs from CreateConversation by allowing + audio transcription and optional DLP redaction. + + .. code-block:: python + + # This snippet has been automatically generated and should be regarded as a + # code template only. + # It will require modifications to work: + # - It may require correct/in-range values for request initialization. + # - It may require specifying regional endpoints when creating the service + # client as shown in: + # https://googleapis.dev/python/google-api-core/latest/client_options.html + from google.cloud import contact_center_insights_v1 + + def sample_upload_conversation(): + # Create a client + client = contact_center_insights_v1.ContactCenterInsightsClient() + + # Initialize request argument(s) + request = contact_center_insights_v1.UploadConversationRequest( + parent="parent_value", + ) + + # Make the request + operation = client.upload_conversation(request=request) + + print("Waiting for operation to complete...") + + response = operation.result() + + # Handle the response + print(response) + + Args: + request (Union[google.cloud.contact_center_insights_v1.types.UploadConversationRequest, dict]): + The request object. Request to upload a conversation. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + 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.contact_center_insights_v1.types.Conversation` + The conversation resource. + + """ + # Create or coerce a protobuf request object. + # Minor optimization to avoid making a copy if the user passes + # in a contact_center_insights.UploadConversationRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, contact_center_insights.UploadConversationRequest): + request = contact_center_insights.UploadConversationRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.upload_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, + ) + + # Wrap the response in an operation future. + response = operation.from_gapic( + response, + self._transport.operations_client, + resources.Conversation, + metadata_type=contact_center_insights.UploadConversationMetadata, + ) + + # Done; return the response. + return response + def update_conversation( self, request: Optional[ diff --git a/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/base.py b/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/base.py index d3ca17d..1be7011 100644 --- a/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/base.py +++ b/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/base.py @@ -132,6 +132,11 @@ def _prep_wrapped_messages(self, client_info): default_timeout=None, client_info=client_info, ), + self.upload_conversation: gapic_v1.method.wrap_method( + self.upload_conversation, + default_timeout=None, + client_info=client_info, + ), self.update_conversation: gapic_v1.method.wrap_method( self.update_conversation, default_timeout=None, @@ -337,6 +342,15 @@ def create_conversation( ]: raise NotImplementedError() + @property + def upload_conversation( + self, + ) -> Callable[ + [contact_center_insights.UploadConversationRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + @property def update_conversation( self, diff --git a/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/grpc.py b/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/grpc.py index b952235..7d3870c 100644 --- a/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/grpc.py +++ b/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/grpc.py @@ -277,6 +277,36 @@ def create_conversation( ) return self._stubs["create_conversation"] + @property + def upload_conversation( + self, + ) -> Callable[ + [contact_center_insights.UploadConversationRequest], operations_pb2.Operation + ]: + r"""Return a callable for the upload conversation method over gRPC. + + Create a longrunning conversation upload operation. + This method differs from CreateConversation by allowing + audio transcription and optional DLP redaction. + + Returns: + Callable[[~.UploadConversationRequest], + ~.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 "upload_conversation" not in self._stubs: + self._stubs["upload_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.contactcenterinsights.v1.ContactCenterInsights/UploadConversation", + request_serializer=contact_center_insights.UploadConversationRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["upload_conversation"] + @property def update_conversation( self, diff --git a/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/grpc_asyncio.py b/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/grpc_asyncio.py index afa6b7a..836561a 100644 --- a/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/grpc_asyncio.py +++ b/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/grpc_asyncio.py @@ -283,6 +283,37 @@ def create_conversation( ) return self._stubs["create_conversation"] + @property + def upload_conversation( + self, + ) -> Callable[ + [contact_center_insights.UploadConversationRequest], + Awaitable[operations_pb2.Operation], + ]: + r"""Return a callable for the upload conversation method over gRPC. + + Create a longrunning conversation upload operation. + This method differs from CreateConversation by allowing + audio transcription and optional DLP redaction. + + Returns: + Callable[[~.UploadConversationRequest], + 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 "upload_conversation" not in self._stubs: + self._stubs["upload_conversation"] = self.grpc_channel.unary_unary( + "/google.cloud.contactcenterinsights.v1.ContactCenterInsights/UploadConversation", + request_serializer=contact_center_insights.UploadConversationRequest.serialize, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["upload_conversation"] + @property def update_conversation( self, diff --git a/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/rest.py b/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/rest.py index 40cfea4..8eab23f 100644 --- a/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/rest.py +++ b/google/cloud/contact_center_insights_v1/services/contact_center_insights/transports/rest.py @@ -352,6 +352,14 @@ def post_update_view(self, response): logging.log(f"Received response: {response}") return response + def pre_upload_conversation(self, request, metadata): + logging.log(f"Received request: {request}") + return request, metadata + + def post_upload_conversation(self, response): + logging.log(f"Received response: {response}") + return response + transport = ContactCenterInsightsRestTransport(interceptor=MyCustomContactCenterInsightsInterceptor()) client = ContactCenterInsightsClient(transport=transport) @@ -1188,6 +1196,31 @@ def post_update_view(self, response: resources.View) -> resources.View: """ return response + def pre_upload_conversation( + self, + request: contact_center_insights.UploadConversationRequest, + metadata: Sequence[Tuple[str, str]], + ) -> Tuple[ + contact_center_insights.UploadConversationRequest, Sequence[Tuple[str, str]] + ]: + """Pre-rpc interceptor for upload_conversation + + Override in a subclass to manipulate the request or metadata + before they are sent to the ContactCenterInsights server. + """ + return request, metadata + + def post_upload_conversation( + self, response: operations_pb2.Operation + ) -> operations_pb2.Operation: + """Post-rpc interceptor for upload_conversation + + Override in a subclass to manipulate the response + after it is returned by the ContactCenterInsights server but before + it is returned to user code. + """ + return response + def pre_cancel_operation( self, request: operations_pb2.CancelOperationRequest, @@ -4739,6 +4772,104 @@ def __call__( resp = self._interceptor.post_update_view(resp) return resp + class _UploadConversation(ContactCenterInsightsRestStub): + def __hash__(self): + return hash("UploadConversation") + + __REQUIRED_FIELDS_DEFAULT_VALUES: Dict[str, Any] = {} + + @classmethod + def _get_unset_required_fields(cls, message_dict): + return { + k: v + for k, v in cls.__REQUIRED_FIELDS_DEFAULT_VALUES.items() + if k not in message_dict + } + + def __call__( + self, + request: contact_center_insights.UploadConversationRequest, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Optional[float] = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operations_pb2.Operation: + r"""Call the upload conversation method over HTTP. + + Args: + request (~.contact_center_insights.UploadConversationRequest): + The request object. Request to upload a conversation. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + ~.operations_pb2.Operation: + This resource represents a + long-running operation that is the + result of a network API call. + + """ + + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{parent=projects/*/locations/*}/conversations:upload", + "body": "*", + }, + ] + request, metadata = self._interceptor.pre_upload_conversation( + request, metadata + ) + pb_request = contact_center_insights.UploadConversationRequest.pb(request) + transcoded_request = path_template.transcode(http_options, pb_request) + + # Jsonify the request body + + body = json_format.MessageToJson( + transcoded_request["body"], + including_default_value_fields=False, + use_integers_for_enums=True, + ) + uri = transcoded_request["uri"] + method = transcoded_request["method"] + + # Jsonify the query params + query_params = json.loads( + json_format.MessageToJson( + transcoded_request["query_params"], + including_default_value_fields=False, + use_integers_for_enums=True, + ) + ) + query_params.update(self._get_unset_required_fields(query_params)) + + query_params["$alt"] = "json;enum-encoding=int" + + # Send the request + headers = dict(metadata) + headers["Content-Type"] = "application/json" + response = getattr(self._session, method)( + "{host}{uri}".format(host=self._host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + data=body, + ) + + # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception + # subclass. + if response.status_code >= 400: + raise core_exceptions.from_http_response(response) + + # Return the response + resp = operations_pb2.Operation() + json_format.Parse(response.content, resp, ignore_unknown_fields=True) + resp = self._interceptor.post_upload_conversation(resp) + return resp + @property def bulk_analyze_conversations( self, @@ -5092,6 +5223,16 @@ def update_view( # In C++ this would require a dynamic_cast return self._UpdateView(self._session, self._host, self._interceptor) # type: ignore + @property + def upload_conversation( + self, + ) -> Callable[ + [contact_center_insights.UploadConversationRequest], operations_pb2.Operation + ]: + # The return type is fine, but mypy isn't sophisticated enough to determine what's going on here. + # In C++ this would require a dynamic_cast + return self._UploadConversation(self._session, self._host, self._interceptor) # type: ignore + @property def cancel_operation(self): return self._CancelOperation(self._session, self._host, self._interceptor) # type: ignore diff --git a/google/cloud/contact_center_insights_v1/types/__init__.py b/google/cloud/contact_center_insights_v1/types/__init__.py index 5422365..d627ec0 100644 --- a/google/cloud/contact_center_insights_v1/types/__init__.py +++ b/google/cloud/contact_center_insights_v1/types/__init__.py @@ -73,6 +73,8 @@ UpdatePhraseMatcherRequest, UpdateSettingsRequest, UpdateViewRequest, + UploadConversationMetadata, + UploadConversationRequest, ) from .resources import ( Analysis, @@ -109,6 +111,7 @@ PhraseMatchRule, PhraseMatchRuleConfig, PhraseMatchRuleGroup, + RedactionConfig, RuntimeAnnotation, SentimentData, Settings, @@ -177,6 +180,8 @@ "UpdatePhraseMatcherRequest", "UpdateSettingsRequest", "UpdateViewRequest", + "UploadConversationMetadata", + "UploadConversationRequest", "ConversationView", "Analysis", "AnalysisResult", @@ -212,6 +217,7 @@ "PhraseMatchRule", "PhraseMatchRuleConfig", "PhraseMatchRuleGroup", + "RedactionConfig", "RuntimeAnnotation", "SentimentData", "Settings", diff --git a/google/cloud/contact_center_insights_v1/types/contact_center_insights.py b/google/cloud/contact_center_insights_v1/types/contact_center_insights.py index 90bf3f2..d020fe9 100644 --- a/google/cloud/contact_center_insights_v1/types/contact_center_insights.py +++ b/google/cloud/contact_center_insights_v1/types/contact_center_insights.py @@ -33,6 +33,8 @@ "CalculateStatsResponse", "CreateAnalysisOperationMetadata", "CreateConversationRequest", + "UploadConversationRequest", + "UploadConversationMetadata", "ListConversationsRequest", "ListConversationsResponse", "GetConversationRequest", @@ -335,6 +337,97 @@ class CreateConversationRequest(proto.Message): ) +class UploadConversationRequest(proto.Message): + r"""Request to upload a conversation. + + Attributes: + parent (str): + Required. The parent resource of the + conversation. + conversation (google.cloud.contact_center_insights_v1.types.Conversation): + Required. The conversation resource to + create. + conversation_id (str): + Optional. A unique ID for the new conversation. This ID will + become the final component of the conversation's resource + name. If no ID is specified, a server-generated ID will be + used. + + This value should be 4-64 characters and must match the + regular expression ``^[a-z0-9-]{4,64}$``. Valid characters + are ``[a-z][0-9]-`` + redaction_config (google.cloud.contact_center_insights_v1.types.RedactionConfig): + Optional. DLP settings for transcript + redaction. Optional, will default to the config + specified in Settings. + """ + + parent: str = proto.Field( + proto.STRING, + number=1, + ) + conversation: resources.Conversation = proto.Field( + proto.MESSAGE, + number=2, + message=resources.Conversation, + ) + conversation_id: str = proto.Field( + proto.STRING, + number=3, + ) + redaction_config: resources.RedactionConfig = proto.Field( + proto.MESSAGE, + number=4, + message=resources.RedactionConfig, + ) + + +class UploadConversationMetadata(proto.Message): + r"""The metadata for an UploadConversation operation. + + Attributes: + create_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The time the operation was + created. + end_time (google.protobuf.timestamp_pb2.Timestamp): + Output only. The time the operation finished + running. + request (google.cloud.contact_center_insights_v1.types.UploadConversationRequest): + Output only. The original request. + analysis_operation (str): + Output only. The operation name for a + successfully created analysis operation, if any. + applied_redaction_config (google.cloud.contact_center_insights_v1.types.RedactionConfig): + Output only. The redaction config applied to + the uploaded conversation. + """ + + create_time: timestamp_pb2.Timestamp = proto.Field( + proto.MESSAGE, + number=1, + message=timestamp_pb2.Timestamp, + ) + end_time: timestamp_pb2.Timestamp = proto.Field( + proto.MESSAGE, + number=2, + message=timestamp_pb2.Timestamp, + ) + request: "UploadConversationRequest" = proto.Field( + proto.MESSAGE, + number=3, + message="UploadConversationRequest", + ) + analysis_operation: str = proto.Field( + proto.STRING, + number=4, + ) + applied_redaction_config: resources.RedactionConfig = proto.Field( + proto.MESSAGE, + number=5, + message=resources.RedactionConfig, + ) + + class ListConversationsRequest(proto.Message): r"""Request to list conversations. diff --git a/google/cloud/contact_center_insights_v1/types/resources.py b/google/cloud/contact_center_insights_v1/types/resources.py index b6c6bdb..0d4137f 100644 --- a/google/cloud/contact_center_insights_v1/types/resources.py +++ b/google/cloud/contact_center_insights_v1/types/resources.py @@ -55,6 +55,7 @@ "PhraseMatchRuleConfig", "ExactMatchConfig", "Settings", + "RedactionConfig", "RuntimeAnnotation", "AnswerFeedback", "ArticleSuggestionData", @@ -1688,6 +1689,9 @@ class Settings(proto.Message): is: projects/{project}/topics/{topic} analysis_config (google.cloud.contact_center_insights_v1.types.Settings.AnalysisConfig): Default analysis settings. + redaction_config (google.cloud.contact_center_insights_v1.types.RedactionConfig): + Default DLP redaction resources to be applied + while ingesting conversations. """ class AnalysisConfig(proto.Message): @@ -1754,6 +1758,35 @@ class AnalysisConfig(proto.Message): number=7, message=AnalysisConfig, ) + redaction_config: "RedactionConfig" = proto.Field( + proto.MESSAGE, + number=10, + message="RedactionConfig", + ) + + +class RedactionConfig(proto.Message): + r"""DLP resources used for redaction while ingesting + conversations. + + Attributes: + deidentify_template (str): + The fully-qualified DLP deidentify template resource name. + Format: + ``projects/{project}/deidentifyTemplates/{template}`` + inspect_template (str): + The fully-qualified DLP inspect template resource name. + Format: ``projects/{project}/inspectTemplates/{template}`` + """ + + deidentify_template: str = proto.Field( + proto.STRING, + number=1, + ) + inspect_template: str = proto.Field( + proto.STRING, + number=2, + ) class RuntimeAnnotation(proto.Message): diff --git a/samples/generated_samples/contactcenterinsights_v1_generated_contact_center_insights_upload_conversation_async.py b/samples/generated_samples/contactcenterinsights_v1_generated_contact_center_insights_upload_conversation_async.py new file mode 100644 index 0000000..4f007e2 --- /dev/null +++ b/samples/generated_samples/contactcenterinsights_v1_generated_contact_center_insights_upload_conversation_async.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 code. DO NOT EDIT! +# +# Snippet for UploadConversation +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-contact-center-insights + + +# [START contactcenterinsights_v1_generated_ContactCenterInsights_UploadConversation_async] +# This snippet has been automatically generated and should be regarded as a +# code template only. +# It will require modifications to work: +# - It may require correct/in-range values for request initialization. +# - It may require specifying regional endpoints when creating the service +# client as shown in: +# https://googleapis.dev/python/google-api-core/latest/client_options.html +from google.cloud import contact_center_insights_v1 + + +async def sample_upload_conversation(): + # Create a client + client = contact_center_insights_v1.ContactCenterInsightsAsyncClient() + + # Initialize request argument(s) + request = contact_center_insights_v1.UploadConversationRequest( + parent="parent_value", + ) + + # Make the request + operation = client.upload_conversation(request=request) + + print("Waiting for operation to complete...") + + response = (await operation).result() + + # Handle the response + print(response) + +# [END contactcenterinsights_v1_generated_ContactCenterInsights_UploadConversation_async] diff --git a/samples/generated_samples/contactcenterinsights_v1_generated_contact_center_insights_upload_conversation_sync.py b/samples/generated_samples/contactcenterinsights_v1_generated_contact_center_insights_upload_conversation_sync.py new file mode 100644 index 0000000..fd78f03 --- /dev/null +++ b/samples/generated_samples/contactcenterinsights_v1_generated_contact_center_insights_upload_conversation_sync.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Copyright 2022 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 code. DO NOT EDIT! +# +# Snippet for UploadConversation +# NOTE: This snippet has been automatically generated for illustrative purposes only. +# It may require modifications to work in your environment. + +# To install the latest published package dependency, execute the following: +# python3 -m pip install google-cloud-contact-center-insights + + +# [START contactcenterinsights_v1_generated_ContactCenterInsights_UploadConversation_sync] +# This snippet has been automatically generated and should be regarded as a +# code template only. +# It will require modifications to work: +# - It may require correct/in-range values for request initialization. +# - It may require specifying regional endpoints when creating the service +# client as shown in: +# https://googleapis.dev/python/google-api-core/latest/client_options.html +from google.cloud import contact_center_insights_v1 + + +def sample_upload_conversation(): + # Create a client + client = contact_center_insights_v1.ContactCenterInsightsClient() + + # Initialize request argument(s) + request = contact_center_insights_v1.UploadConversationRequest( + parent="parent_value", + ) + + # Make the request + operation = client.upload_conversation(request=request) + + print("Waiting for operation to complete...") + + response = operation.result() + + # Handle the response + print(response) + +# [END contactcenterinsights_v1_generated_ContactCenterInsights_UploadConversation_sync] diff --git a/samples/generated_samples/snippet_metadata_google.cloud.contactcenterinsights.v1.json b/samples/generated_samples/snippet_metadata_google.cloud.contactcenterinsights.v1.json index 1faa7a2..c3e9d3d 100644 --- a/samples/generated_samples/snippet_metadata_google.cloud.contactcenterinsights.v1.json +++ b/samples/generated_samples/snippet_metadata_google.cloud.contactcenterinsights.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-contact-center-insights", - "version": "1.9.1" + "version": "0.1.0" }, "snippets": [ { @@ -6049,6 +6049,159 @@ } ], "title": "contactcenterinsights_v1_generated_contact_center_insights_update_view_sync.py" + }, + { + "canonical": true, + "clientMethod": { + "async": true, + "client": { + "fullName": "google.cloud.contact_center_insights_v1.ContactCenterInsightsAsyncClient", + "shortName": "ContactCenterInsightsAsyncClient" + }, + "fullName": "google.cloud.contact_center_insights_v1.ContactCenterInsightsAsyncClient.upload_conversation", + "method": { + "fullName": "google.cloud.contactcenterinsights.v1.ContactCenterInsights.UploadConversation", + "service": { + "fullName": "google.cloud.contactcenterinsights.v1.ContactCenterInsights", + "shortName": "ContactCenterInsights" + }, + "shortName": "UploadConversation" + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.contact_center_insights_v1.types.UploadConversationRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.api_core.operation_async.AsyncOperation", + "shortName": "upload_conversation" + }, + "description": "Sample for UploadConversation", + "file": "contactcenterinsights_v1_generated_contact_center_insights_upload_conversation_async.py", + "language": "PYTHON", + "origin": "API_DEFINITION", + "regionTag": "contactcenterinsights_v1_generated_ContactCenterInsights_UploadConversation_async", + "segments": [ + { + "end": 55, + "start": 27, + "type": "FULL" + }, + { + "end": 55, + "start": 27, + "type": "SHORT" + }, + { + "end": 40, + "start": 38, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 45, + "start": 41, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 52, + "start": 46, + "type": "REQUEST_EXECUTION" + }, + { + "end": 56, + "start": 53, + "type": "RESPONSE_HANDLING" + } + ], + "title": "contactcenterinsights_v1_generated_contact_center_insights_upload_conversation_async.py" + }, + { + "canonical": true, + "clientMethod": { + "client": { + "fullName": "google.cloud.contact_center_insights_v1.ContactCenterInsightsClient", + "shortName": "ContactCenterInsightsClient" + }, + "fullName": "google.cloud.contact_center_insights_v1.ContactCenterInsightsClient.upload_conversation", + "method": { + "fullName": "google.cloud.contactcenterinsights.v1.ContactCenterInsights.UploadConversation", + "service": { + "fullName": "google.cloud.contactcenterinsights.v1.ContactCenterInsights", + "shortName": "ContactCenterInsights" + }, + "shortName": "UploadConversation" + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.contact_center_insights_v1.types.UploadConversationRequest" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, str]" + } + ], + "resultType": "google.api_core.operation.Operation", + "shortName": "upload_conversation" + }, + "description": "Sample for UploadConversation", + "file": "contactcenterinsights_v1_generated_contact_center_insights_upload_conversation_sync.py", + "language": "PYTHON", + "origin": "API_DEFINITION", + "regionTag": "contactcenterinsights_v1_generated_ContactCenterInsights_UploadConversation_sync", + "segments": [ + { + "end": 55, + "start": 27, + "type": "FULL" + }, + { + "end": 55, + "start": 27, + "type": "SHORT" + }, + { + "end": 40, + "start": 38, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 45, + "start": 41, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 52, + "start": 46, + "type": "REQUEST_EXECUTION" + }, + { + "end": 56, + "start": 53, + "type": "RESPONSE_HANDLING" + } + ], + "title": "contactcenterinsights_v1_generated_contact_center_insights_upload_conversation_sync.py" } ] } diff --git a/scripts/fixup_contact_center_insights_v1_keywords.py b/scripts/fixup_contact_center_insights_v1_keywords.py index 01d4a4f..0238d9e 100644 --- a/scripts/fixup_contact_center_insights_v1_keywords.py +++ b/scripts/fixup_contact_center_insights_v1_keywords.py @@ -76,6 +76,7 @@ class contact_center_insightsCallTransformer(cst.CSTTransformer): 'update_phrase_matcher': ('phrase_matcher', 'update_mask', ), 'update_settings': ('settings', 'update_mask', ), 'update_view': ('view', 'update_mask', ), + 'upload_conversation': ('parent', 'conversation', 'conversation_id', 'redaction_config', ), } def leave_Call(self, original: cst.Call, updated: cst.Call) -> cst.CSTNode: diff --git a/tests/unit/gapic/contact_center_insights_v1/test_contact_center_insights.py b/tests/unit/gapic/contact_center_insights_v1/test_contact_center_insights.py index 7ecd62c..23aa7b0 100644 --- a/tests/unit/gapic/contact_center_insights_v1/test_contact_center_insights.py +++ b/tests/unit/gapic/contact_center_insights_v1/test_contact_center_insights.py @@ -1066,6 +1066,161 @@ async def test_create_conversation_flattened_error_async(): ) +@pytest.mark.parametrize( + "request_type", + [ + contact_center_insights.UploadConversationRequest, + dict, + ], +) +def test_upload_conversation(request_type, transport: str = "grpc"): + client = ContactCenterInsightsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.upload_conversation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation(name="operations/spam") + response = client.upload_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] == contact_center_insights.UploadConversationRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +def test_upload_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 = ContactCenterInsightsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.upload_conversation), "__call__" + ) as call: + client.upload_conversation() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == contact_center_insights.UploadConversationRequest() + + +@pytest.mark.asyncio +async def test_upload_conversation_async( + transport: str = "grpc_asyncio", + request_type=contact_center_insights.UploadConversationRequest, +): + client = ContactCenterInsightsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = request_type() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.upload_conversation), "__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.upload_conversation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == contact_center_insights.UploadConversationRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, future.Future) + + +@pytest.mark.asyncio +async def test_upload_conversation_async_from_dict(): + await test_upload_conversation_async(request_type=dict) + + +def test_upload_conversation_field_headers(): + client = ContactCenterInsightsClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = contact_center_insights.UploadConversationRequest() + + request.parent = "parent_value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.upload_conversation), "__call__" + ) as call: + call.return_value = operations_pb2.Operation(name="operations/op") + client.upload_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_upload_conversation_field_headers_async(): + client = ContactCenterInsightsAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = contact_center_insights.UploadConversationRequest() + + request.parent = "parent_value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.upload_conversation), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation(name="operations/op") + ) + await client.upload_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"] + + @pytest.mark.parametrize( "request_type", [ @@ -11271,6 +11426,224 @@ def test_create_conversation_rest_error(): ) +@pytest.mark.parametrize( + "request_type", + [ + contact_center_insights.UploadConversationRequest, + dict, + ], +) +def test_upload_conversation_rest(request_type): + client = ContactCenterInsightsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # send a request that will satisfy transcoding + request_init = {"parent": "projects/sample1/locations/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a response. + with mock.patch.object(type(client.transport._session), "request") as req: + # Designate an appropriate value for the returned response. + return_value = operations_pb2.Operation(name="operations/spam") + + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + + response_value._content = json_return_value.encode("UTF-8") + req.return_value = response_value + response = client.upload_conversation(request) + + # Establish that the response is the type that we expect. + assert response.operation.name == "operations/spam" + + +def test_upload_conversation_rest_required_fields( + request_type=contact_center_insights.UploadConversationRequest, +): + transport_class = transports.ContactCenterInsightsRestTransport + + request_init = {} + request_init["parent"] = "" + request = request_type(**request_init) + pb_request = request_type.pb(request) + jsonified_request = json.loads( + json_format.MessageToJson( + pb_request, + including_default_value_fields=False, + use_integers_for_enums=False, + ) + ) + + # verify fields with default values are dropped + + unset_fields = transport_class( + credentials=ga_credentials.AnonymousCredentials() + ).upload_conversation._get_unset_required_fields(jsonified_request) + jsonified_request.update(unset_fields) + + # verify required fields with default values are now present + + jsonified_request["parent"] = "parent_value" + + unset_fields = transport_class( + credentials=ga_credentials.AnonymousCredentials() + ).upload_conversation._get_unset_required_fields(jsonified_request) + jsonified_request.update(unset_fields) + + # verify required fields with non-default values are left alone + assert "parent" in jsonified_request + assert jsonified_request["parent"] == "parent_value" + + client = ContactCenterInsightsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type(**request_init) + + # Designate an appropriate value for the returned response. + return_value = operations_pb2.Operation(name="operations/spam") + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "request") as req: + # We need to mock transcode() because providing default values + # for required fields will fail the real version if the http_options + # expect actual values for those fields. + with mock.patch.object(path_template, "transcode") as transcode: + # A uri without fields and an empty body will force all the + # request fields to show up in the query_params. + pb_request = request_type.pb(request) + transcode_result = { + "uri": "v1/sample_method", + "method": "post", + "query_params": pb_request, + } + transcode_result["body"] = pb_request + transcode.return_value = transcode_result + + response_value = Response() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + + response_value._content = json_return_value.encode("UTF-8") + req.return_value = response_value + + response = client.upload_conversation(request) + + expected_params = [("$alt", "json;enum-encoding=int")] + actual_params = req.call_args.kwargs["params"] + assert expected_params == actual_params + + +def test_upload_conversation_rest_unset_required_fields(): + transport = transports.ContactCenterInsightsRestTransport( + credentials=ga_credentials.AnonymousCredentials + ) + + unset_fields = transport.upload_conversation._get_unset_required_fields({}) + assert set(unset_fields) == ( + set(()) + & set( + ( + "parent", + "conversation", + ) + ) + ) + + +@pytest.mark.parametrize("null_interceptor", [True, False]) +def test_upload_conversation_rest_interceptors(null_interceptor): + transport = transports.ContactCenterInsightsRestTransport( + credentials=ga_credentials.AnonymousCredentials(), + interceptor=None + if null_interceptor + else transports.ContactCenterInsightsRestInterceptor(), + ) + client = ContactCenterInsightsClient(transport=transport) + with mock.patch.object( + type(client.transport._session), "request" + ) as req, mock.patch.object( + path_template, "transcode" + ) as transcode, mock.patch.object( + operation.Operation, "_set_result_from_operation" + ), mock.patch.object( + transports.ContactCenterInsightsRestInterceptor, "post_upload_conversation" + ) as post, mock.patch.object( + transports.ContactCenterInsightsRestInterceptor, "pre_upload_conversation" + ) as pre: + pre.assert_not_called() + post.assert_not_called() + pb_message = contact_center_insights.UploadConversationRequest.pb( + contact_center_insights.UploadConversationRequest() + ) + transcode.return_value = { + "method": "post", + "uri": "my_uri", + "body": pb_message, + "query_params": pb_message, + } + + req.return_value = Response() + req.return_value.status_code = 200 + req.return_value.request = PreparedRequest() + req.return_value._content = json_format.MessageToJson( + operations_pb2.Operation() + ) + + request = contact_center_insights.UploadConversationRequest() + metadata = [ + ("key", "val"), + ("cephalopod", "squid"), + ] + pre.return_value = request, metadata + post.return_value = operations_pb2.Operation() + + client.upload_conversation( + request, + metadata=[ + ("key", "val"), + ("cephalopod", "squid"), + ], + ) + + pre.assert_called_once() + post.assert_called_once() + + +def test_upload_conversation_rest_bad_request( + transport: str = "rest", + request_type=contact_center_insights.UploadConversationRequest, +): + client = ContactCenterInsightsClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # send a request that will satisfy transcoding + request_init = {"parent": "projects/sample1/locations/sample2"} + request = request_type(**request_init) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(Session, "request") as req, pytest.raises( + core_exceptions.BadRequest + ): + # Wrap the value into a proper Response obj + response_value = Response() + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + client.upload_conversation(request) + + +def test_upload_conversation_rest_error(): + client = ContactCenterInsightsClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + @pytest.mark.parametrize( "request_type", [ @@ -20426,6 +20799,10 @@ def test_update_settings_rest(request_type): "issue_models": ["issue_models_value1", "issue_models_value2"], }, }, + "redaction_config": { + "deidentify_template": "deidentify_template_value", + "inspect_template": "inspect_template_value", + }, } request = request_type(**request_init) @@ -20632,6 +21009,10 @@ def test_update_settings_rest_bad_request( "issue_models": ["issue_models_value1", "issue_models_value2"], }, }, + "redaction_config": { + "deidentify_template": "deidentify_template_value", + "inspect_template": "inspect_template_value", + }, } request = request_type(**request_init) @@ -22296,6 +22677,7 @@ def test_contact_center_insights_base_transport(): # raise NotImplementedError. methods = ( "create_conversation", + "upload_conversation", "update_conversation", "get_conversation", "list_conversations", @@ -22622,6 +23004,9 @@ def test_contact_center_insights_client_transport_session_collision(transport_na session1 = client1.transport.create_conversation._session session2 = client2.transport.create_conversation._session assert session1 != session2 + session1 = client1.transport.upload_conversation._session + session2 = client2.transport.upload_conversation._session + assert session1 != session2 session1 = client1.transport.update_conversation._session session2 = client2.transport.update_conversation._session assert session1 != session2