From 451f683eb714a73628388a23665f87202bbd4777 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 20 Aug 2021 23:02:55 +0000 Subject: [PATCH 1/4] fix: do not error on LROs with no response or error --- google/api_core/operation.py | 12 ++++++------ google/api_core/operation_async.py | 11 +++++------ tests/asyncio/test_operation_async.py | 6 +++--- tests/unit/test_operation.py | 6 ++---- 4 files changed, 16 insertions(+), 19 deletions(-) diff --git a/google/api_core/operation.py b/google/api_core/operation.py index b17f753b..039d75b8 100644 --- a/google/api_core/operation.py +++ b/google/api_core/operation.py @@ -139,12 +139,12 @@ def _set_result_from_operation(self): response=self._operation, ) self.set_exception(exception) - else: - exception = exceptions.GoogleAPICallError( - "Unexpected state: Long-running operation had neither " - "response nor error set." - ) - self.set_exception(exception) + elif self._operation.done: + # Some APIs set `done: true`, with an empty response. + # Set the result to an empty message of the expected + # result type. + # https://google.aip.dev/151 + self.set_result(self._result_type()) def _refresh_and_update(self, retry=polling.DEFAULT_RETRY): """Refresh the operation and update the result if needed. diff --git a/google/api_core/operation_async.py b/google/api_core/operation_async.py index 6bae8654..bd292382 100644 --- a/google/api_core/operation_async.py +++ b/google/api_core/operation_async.py @@ -135,12 +135,11 @@ def _set_result_from_operation(self): response=self._operation, ) self.set_exception(exception) - else: - exception = exceptions.GoogleAPICallError( - "Unexpected state: Long-running operation had neither " - "response nor error set." - ) - self.set_exception(exception) + elif self._operation.done: + # Some APIs set `done: true`, with an empty response. + # Set the result to an empty message of the expected + # result type. + self.set_result(self._result_type()) async def _refresh_and_update(self, retry=async_future.DEFAULT_RETRY): """Refresh the operation and update the result if needed. diff --git a/tests/asyncio/test_operation_async.py b/tests/asyncio/test_operation_async.py index 907cda7c..342184fb 100644 --- a/tests/asyncio/test_operation_async.py +++ b/tests/asyncio/test_operation_async.py @@ -153,7 +153,7 @@ async def test_exception(): @mock.patch("asyncio.sleep", autospec=True) @pytest.mark.asyncio -async def test_unexpected_result(unused_sleep): +async def test_done_with_no_error_or_response(unused_sleep): responses = [ make_operation_proto(), # Second operation response is done, but has not error or response. @@ -161,9 +161,9 @@ async def test_unexpected_result(unused_sleep): ] future, _, _ = make_operation_future(responses) - exception = await future.exception() + result = await future.result() - assert "Unexpected state" in "{!r}".format(exception) + assert isinstance(result, struct_pb2.Struct) def test_from_gapic(): diff --git a/tests/unit/test_operation.py b/tests/unit/test_operation.py index 28fbfe27..7a3e3c6c 100644 --- a/tests/unit/test_operation.py +++ b/tests/unit/test_operation.py @@ -163,7 +163,7 @@ def test_exception_with_error_code(): assert isinstance(exception, exceptions.NotFound) -def test_unexpected_result(): +def test_done_with_no_error_or_response(): responses = [ make_operation_proto(), # Second operation response is done, but has not error or response. @@ -171,9 +171,7 @@ def test_unexpected_result(): ] future, _, _ = make_operation_future(responses) - exception = future.exception() - - assert "Unexpected state" in "{!r}".format(exception) + assert isinstance(future.result(), struct_pb2.Struct) def test__refresh_http(): From 003723db9f80f01c6bde0a7af0133ccc17f59013 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 20 Aug 2021 23:05:58 +0000 Subject: [PATCH 2/4] chore: add aip to async operation --- export_to_bigquery.py | 24 ++++++++++++++++++++++++ google/api_core/operation_async.py | 1 + 2 files changed, 25 insertions(+) create mode 100644 export_to_bigquery.py diff --git a/export_to_bigquery.py b/export_to_bigquery.py new file mode 100644 index 00000000..df0afa83 --- /dev/null +++ b/export_to_bigquery.py @@ -0,0 +1,24 @@ +from google.cloud import contact_center_insights_v1 + + +project_id = "busun-sandbox" +bigquery_project_id = "busun-sandbox" +bigquery_table = "ccai-table" +bigquery_dataset = "test" + +request = contact_center_insights_v1.ExportInsightsDataRequest() + +request.parent = contact_center_insights_v1.ContactCenterInsightsClient.common_location_path(project_id, "us-central1") +request.big_query_destination.project_id = bigquery_project_id +request.big_query_destination.table = bigquery_table +request.big_query_destination.dataset = bigquery_dataset + +print(request) + +# Call the Insights client to export data to BigQuery. +insights_client = contact_center_insights_v1.ContactCenterInsightsClient() +export_operation = insights_client.export_insights_data(request=request) + +print("Waiting for results...") +result = export_operation.result(500) +print(result) diff --git a/google/api_core/operation_async.py b/google/api_core/operation_async.py index bd292382..816b674c 100644 --- a/google/api_core/operation_async.py +++ b/google/api_core/operation_async.py @@ -139,6 +139,7 @@ def _set_result_from_operation(self): # Some APIs set `done: true`, with an empty response. # Set the result to an empty message of the expected # result type. + # https://google.aip.dev/151 self.set_result(self._result_type()) async def _refresh_and_update(self, retry=async_future.DEFAULT_RETRY): From d4312714d0b67efbc3ccd6568838c6a4140a0112 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Fri, 20 Aug 2021 23:06:28 +0000 Subject: [PATCH 3/4] chore: delete sample --- export_to_bigquery.py | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 export_to_bigquery.py diff --git a/export_to_bigquery.py b/export_to_bigquery.py deleted file mode 100644 index df0afa83..00000000 --- a/export_to_bigquery.py +++ /dev/null @@ -1,24 +0,0 @@ -from google.cloud import contact_center_insights_v1 - - -project_id = "busun-sandbox" -bigquery_project_id = "busun-sandbox" -bigquery_table = "ccai-table" -bigquery_dataset = "test" - -request = contact_center_insights_v1.ExportInsightsDataRequest() - -request.parent = contact_center_insights_v1.ContactCenterInsightsClient.common_location_path(project_id, "us-central1") -request.big_query_destination.project_id = bigquery_project_id -request.big_query_destination.table = bigquery_table -request.big_query_destination.dataset = bigquery_dataset - -print(request) - -# Call the Insights client to export data to BigQuery. -insights_client = contact_center_insights_v1.ContactCenterInsightsClient() -export_operation = insights_client.export_insights_data(request=request) - -print("Waiting for results...") -result = export_operation.result(500) -print(result) From d101d6f1157a1a6e2f299ddf0db03049336712c8 Mon Sep 17 00:00:00 2001 From: Bu Sun Kim Date: Tue, 31 Aug 2021 16:18:22 +0000 Subject: [PATCH 4/4] fix: use 'else' instead of elif --- google/api_core/operation.py | 2 +- google/api_core/operation_async.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/google/api_core/operation.py b/google/api_core/operation.py index 039d75b8..a66e4a50 100644 --- a/google/api_core/operation.py +++ b/google/api_core/operation.py @@ -139,7 +139,7 @@ def _set_result_from_operation(self): response=self._operation, ) self.set_exception(exception) - elif self._operation.done: + else: # Some APIs set `done: true`, with an empty response. # Set the result to an empty message of the expected # result type. diff --git a/google/api_core/operation_async.py b/google/api_core/operation_async.py index 816b674c..17624d62 100644 --- a/google/api_core/operation_async.py +++ b/google/api_core/operation_async.py @@ -135,7 +135,7 @@ def _set_result_from_operation(self): response=self._operation, ) self.set_exception(exception) - elif self._operation.done: + else: # Some APIs set `done: true`, with an empty response. # Set the result to an empty message of the expected # result type.