diff --git a/gapic/templates/%namespace/%name_%version/%sub/services/%service/_shared_macros.j2 b/gapic/templates/%namespace/%name_%version/%sub/services/%service/_shared_macros.j2 index 37aa5c9ed..36be0a440 100644 --- a/gapic/templates/%namespace/%name_%version/%sub/services/%service/_shared_macros.j2 +++ b/gapic/templates/%namespace/%name_%version/%sub/services/%service/_shared_macros.j2 @@ -353,8 +353,6 @@ class {{ async_method_name_prefix }}{{ service.name }}RestInterceptor: {% endif %}{# if (not is_async) or (is_async and not method.lro and not method.extended_lro and not method.paged_result_field) #} {% endfor %} - {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2148): Remove the condition below once mixins are supported for async rest transport. #} - {% if not is_async %} {% for name, signature in api.mixin_api_signatures.items() %} {{ async_prefix }}def pre_{{ name|snake_case }}( self, request: {{signature.request_type}}, metadata: Sequence[Tuple[str, str]] @@ -377,7 +375,6 @@ class {{ async_method_name_prefix }}{{ service.name }}RestInterceptor: """ return response {% endfor %} - {% endif %} {% endmacro %} {% macro generate_mixin_call_method(service, api, name, sig, is_async) %} diff --git a/gapic/templates/%namespace/%name_%version/%sub/services/%service/async_client.py.j2 b/gapic/templates/%namespace/%name_%version/%sub/services/%service/async_client.py.j2 index 1abddd198..2304eee73 100644 --- a/gapic/templates/%namespace/%name_%version/%sub/services/%service/async_client.py.j2 +++ b/gapic/templates/%namespace/%name_%version/%sub/services/%service/async_client.py.j2 @@ -21,6 +21,7 @@ from google.api_core.client_options import ClientOptions from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 from google.api_core import retry_async as retries +import google.auth from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore diff --git a/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 b/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 index 25eb4d57f..3c55a2e2b 100644 --- a/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 +++ b/gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/rest_asyncio.py.j2 @@ -225,6 +225,9 @@ class Async{{service.name}}RestTransport(_Base{{ service.name }}RestTransport): return self._{{method.name}}(self._session, self._host, self._interceptor) # type: ignore {% endfor %} + {% for name, sig in api.mixin_api_signatures.items() %} + {{ shared_macros.generate_mixin_call_method(service, api, name, sig, is_async=True) | indent(4) }} + {% endfor %} @property def kind(self) -> str: diff --git a/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_macros.j2 b/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_macros.j2 index b912d4845..bce866c54 100644 --- a/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_macros.j2 +++ b/gapic/templates/tests/unit/gapic/%name_%version/%sub/test_macros.j2 @@ -1884,11 +1884,8 @@ def test_unsupported_parameter_rest_asyncio(): {% endfor %}{# for method in service.methods.values() #} {% for name, sig in api.mixin_api_signatures.items() %} {% if 'rest' in transport %} -{# TODO: Remove the following condition as we implement async mixins #} -{% if not is_async %} {{ bad_request_mixins_test(service, api, name, sig, transport, is_async) }} {{ call_success_mixins_test(service, api, name, sig, transport, is_async) }} -{% endif %}{# if not is_async #} {% endif %}{# if 'rest' in transport #} {% endfor %} {{ initialize_client_with_transport_test(service, transport, is_async) }} diff --git a/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/async_client.py b/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/async_client.py index 6aef1e816..2e8fe5d67 100755 --- a/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/async_client.py +++ b/tests/integration/goldens/asset/google/cloud/asset_v1/services/asset_service/async_client.py @@ -23,6 +23,7 @@ from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 from google.api_core import retry_async as retries +import google.auth from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore diff --git a/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/async_client.py b/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/async_client.py index 485c5bdee..42fe26778 100755 --- a/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/async_client.py +++ b/tests/integration/goldens/credentials/google/iam/credentials_v1/services/iam_credentials/async_client.py @@ -23,6 +23,7 @@ from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 from google.api_core import retry_async as retries +import google.auth from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore diff --git a/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/async_client.py b/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/async_client.py index 7252f19d5..44bc02b13 100755 --- a/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/async_client.py +++ b/tests/integration/goldens/eventarc/google/cloud/eventarc_v1/services/eventarc/async_client.py @@ -23,6 +23,7 @@ from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 from google.api_core import retry_async as retries +import google.auth from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore diff --git a/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/async_client.py b/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/async_client.py index dbe7f7077..86aecd441 100755 --- a/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/async_client.py +++ b/tests/integration/goldens/logging/google/cloud/logging_v2/services/config_service_v2/async_client.py @@ -23,6 +23,7 @@ from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 from google.api_core import retry_async as retries +import google.auth from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore diff --git a/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/async_client.py b/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/async_client.py index d8c1d2284..8ee67242b 100755 --- a/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/async_client.py +++ b/tests/integration/goldens/logging/google/cloud/logging_v2/services/logging_service_v2/async_client.py @@ -23,6 +23,7 @@ from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 from google.api_core import retry_async as retries +import google.auth from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore diff --git a/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/async_client.py b/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/async_client.py index 70a383b96..02819a1ec 100755 --- a/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/async_client.py +++ b/tests/integration/goldens/logging/google/cloud/logging_v2/services/metrics_service_v2/async_client.py @@ -23,6 +23,7 @@ from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 from google.api_core import retry_async as retries +import google.auth from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore diff --git a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/async_client.py b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/async_client.py index ca71e086e..b1beaf561 100755 --- a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/async_client.py +++ b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/async_client.py @@ -23,6 +23,7 @@ from google.api_core import exceptions as core_exceptions from google.api_core import gapic_v1 from google.api_core import retry_async as retries +import google.auth from google.auth import credentials as ga_credentials # type: ignore from google.oauth2 import service_account # type: ignore diff --git a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py index 6c63e4763..963f85599 100755 --- a/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py +++ b/tests/integration/goldens/redis/google/cloud/redis_v1/services/cloud_redis/transports/rest_asyncio.py @@ -205,6 +205,127 @@ async def post_get_instance_auth_string(self, response: cloud_redis.InstanceAuth """ return response + async def pre_get_location( + self, request: locations_pb2.GetLocationRequest, metadata: Sequence[Tuple[str, str]] + ) -> Tuple[locations_pb2.GetLocationRequest, Sequence[Tuple[str, str]]]: + """Pre-rpc interceptor for get_location + + Override in a subclass to manipulate the request or metadata + before they are sent to the CloudRedis server. + """ + return request, metadata + + async def post_get_location( + self, response: locations_pb2.Location + ) -> locations_pb2.Location: + """Post-rpc interceptor for get_location + + Override in a subclass to manipulate the response + after it is returned by the CloudRedis server but before + it is returned to user code. + """ + return response + async def pre_list_locations( + self, request: locations_pb2.ListLocationsRequest, metadata: Sequence[Tuple[str, str]] + ) -> Tuple[locations_pb2.ListLocationsRequest, Sequence[Tuple[str, str]]]: + """Pre-rpc interceptor for list_locations + + Override in a subclass to manipulate the request or metadata + before they are sent to the CloudRedis server. + """ + return request, metadata + + async def post_list_locations( + self, response: locations_pb2.ListLocationsResponse + ) -> locations_pb2.ListLocationsResponse: + """Post-rpc interceptor for list_locations + + Override in a subclass to manipulate the response + after it is returned by the CloudRedis server but before + it is returned to user code. + """ + return response + async def pre_cancel_operation( + self, request: operations_pb2.CancelOperationRequest, metadata: Sequence[Tuple[str, str]] + ) -> Tuple[operations_pb2.CancelOperationRequest, Sequence[Tuple[str, str]]]: + """Pre-rpc interceptor for cancel_operation + + Override in a subclass to manipulate the request or metadata + before they are sent to the CloudRedis server. + """ + return request, metadata + + async def post_cancel_operation( + self, response: None + ) -> None: + """Post-rpc interceptor for cancel_operation + + Override in a subclass to manipulate the response + after it is returned by the CloudRedis server but before + it is returned to user code. + """ + return response + async def pre_delete_operation( + self, request: operations_pb2.DeleteOperationRequest, metadata: Sequence[Tuple[str, str]] + ) -> Tuple[operations_pb2.DeleteOperationRequest, Sequence[Tuple[str, str]]]: + """Pre-rpc interceptor for delete_operation + + Override in a subclass to manipulate the request or metadata + before they are sent to the CloudRedis server. + """ + return request, metadata + + async def post_delete_operation( + self, response: None + ) -> None: + """Post-rpc interceptor for delete_operation + + Override in a subclass to manipulate the response + after it is returned by the CloudRedis server but before + it is returned to user code. + """ + return response + async def pre_get_operation( + self, request: operations_pb2.GetOperationRequest, metadata: Sequence[Tuple[str, str]] + ) -> Tuple[operations_pb2.GetOperationRequest, Sequence[Tuple[str, str]]]: + """Pre-rpc interceptor for get_operation + + Override in a subclass to manipulate the request or metadata + before they are sent to the CloudRedis server. + """ + return request, metadata + + async def post_get_operation( + self, response: operations_pb2.Operation + ) -> operations_pb2.Operation: + """Post-rpc interceptor for get_operation + + Override in a subclass to manipulate the response + after it is returned by the CloudRedis server but before + it is returned to user code. + """ + return response + async def pre_list_operations( + self, request: operations_pb2.ListOperationsRequest, metadata: Sequence[Tuple[str, str]] + ) -> Tuple[operations_pb2.ListOperationsRequest, Sequence[Tuple[str, str]]]: + """Pre-rpc interceptor for list_operations + + Override in a subclass to manipulate the request or metadata + before they are sent to the CloudRedis server. + """ + return request, metadata + + async def post_list_operations( + self, response: operations_pb2.ListOperationsResponse + ) -> operations_pb2.ListOperationsResponse: + """Post-rpc interceptor for list_operations + + Override in a subclass to manipulate the response + after it is returned by the CloudRedis server but before + it is returned to user code. + """ + return response + @dataclasses.dataclass class AsyncCloudRedisRestStub: @@ -727,6 +848,454 @@ def upgrade_instance(self) -> Callable[ operations_pb2.Operation]: return self._UpgradeInstance(self._session, self._host, self._interceptor) # type: ignore + @property + def get_location(self): + return self._GetLocation(self._session, self._host, self._interceptor) # type: ignore + + class _GetLocation(_BaseCloudRedisRestTransport._BaseGetLocation, AsyncCloudRedisRestStub): + def __hash__(self): + return hash("AsyncCloudRedisRestTransport.GetLocation") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None): + + uri = transcoded_request['uri'] + method = transcoded_request['method'] + headers = dict(metadata) + headers['Content-Type'] = 'application/json' + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response + + async def __call__(self, + request: locations_pb2.GetLocationRequest, *, + retry: OptionalRetry=gapic_v1.method.DEFAULT, + timeout: Optional[float]=None, + metadata: Sequence[Tuple[str, str]]=(), + ) -> locations_pb2.Location: + + r"""Call the get location method over HTTP. + + Args: + request (locations_pb2.GetLocationRequest): + The request object for GetLocation method. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, + should be 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: + locations_pb2.Location: Response from GetLocation method. + """ + + http_options = _BaseCloudRedisRestTransport._BaseGetLocation._get_http_options() + request, metadata = await self._interceptor.pre_get_location(request, metadata) + transcoded_request = _BaseCloudRedisRestTransport._BaseGetLocation._get_transcoded_request(http_options, request) + + # Jsonify the query params + query_params = _BaseCloudRedisRestTransport._BaseGetLocation._get_query_params_json(transcoded_request) + + # Send the request + response = await AsyncCloudRedisRestTransport._GetLocation._get_response(self._host, metadata, query_params, self._session, timeout, transcoded_request) + + # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception + # subclass. + if response.status_code >= 400: + content = await response.read() + payload = json.loads(content.decode('utf-8')) + request_url = "{host}{uri}".format(host=self._host, uri=transcoded_request['uri']) + method = transcoded_request['method'] + raise core_exceptions.format_http_response_error(response, method, request_url, payload) # type: ignore + + content = await response.read() + resp = locations_pb2.Location() + resp = json_format.Parse(content, resp) + resp = await self._interceptor.post_get_location(resp) + return resp + + @property + def list_locations(self): + return self._ListLocations(self._session, self._host, self._interceptor) # type: ignore + + class _ListLocations(_BaseCloudRedisRestTransport._BaseListLocations, AsyncCloudRedisRestStub): + def __hash__(self): + return hash("AsyncCloudRedisRestTransport.ListLocations") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None): + + uri = transcoded_request['uri'] + method = transcoded_request['method'] + headers = dict(metadata) + headers['Content-Type'] = 'application/json' + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response + + async def __call__(self, + request: locations_pb2.ListLocationsRequest, *, + retry: OptionalRetry=gapic_v1.method.DEFAULT, + timeout: Optional[float]=None, + metadata: Sequence[Tuple[str, str]]=(), + ) -> locations_pb2.ListLocationsResponse: + + r"""Call the list locations method over HTTP. + + Args: + request (locations_pb2.ListLocationsRequest): + The request object for ListLocations method. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, + should be 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: + locations_pb2.ListLocationsResponse: Response from ListLocations method. + """ + + http_options = _BaseCloudRedisRestTransport._BaseListLocations._get_http_options() + request, metadata = await self._interceptor.pre_list_locations(request, metadata) + transcoded_request = _BaseCloudRedisRestTransport._BaseListLocations._get_transcoded_request(http_options, request) + + # Jsonify the query params + query_params = _BaseCloudRedisRestTransport._BaseListLocations._get_query_params_json(transcoded_request) + + # Send the request + response = await AsyncCloudRedisRestTransport._ListLocations._get_response(self._host, metadata, query_params, self._session, timeout, transcoded_request) + + # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception + # subclass. + if response.status_code >= 400: + content = await response.read() + payload = json.loads(content.decode('utf-8')) + request_url = "{host}{uri}".format(host=self._host, uri=transcoded_request['uri']) + method = transcoded_request['method'] + raise core_exceptions.format_http_response_error(response, method, request_url, payload) # type: ignore + + content = await response.read() + resp = locations_pb2.ListLocationsResponse() + resp = json_format.Parse(content, resp) + resp = await self._interceptor.post_list_locations(resp) + return resp + + @property + def cancel_operation(self): + return self._CancelOperation(self._session, self._host, self._interceptor) # type: ignore + + class _CancelOperation(_BaseCloudRedisRestTransport._BaseCancelOperation, AsyncCloudRedisRestStub): + def __hash__(self): + return hash("AsyncCloudRedisRestTransport.CancelOperation") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None): + + uri = transcoded_request['uri'] + method = transcoded_request['method'] + headers = dict(metadata) + headers['Content-Type'] = 'application/json' + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response + + async def __call__(self, + request: operations_pb2.CancelOperationRequest, *, + retry: OptionalRetry=gapic_v1.method.DEFAULT, + timeout: Optional[float]=None, + metadata: Sequence[Tuple[str, str]]=(), + ) -> None: + + r"""Call the cancel operation method over HTTP. + + Args: + request (operations_pb2.CancelOperationRequest): + The request object for CancelOperation method. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + + http_options = _BaseCloudRedisRestTransport._BaseCancelOperation._get_http_options() + request, metadata = await self._interceptor.pre_cancel_operation(request, metadata) + transcoded_request = _BaseCloudRedisRestTransport._BaseCancelOperation._get_transcoded_request(http_options, request) + + # Jsonify the query params + query_params = _BaseCloudRedisRestTransport._BaseCancelOperation._get_query_params_json(transcoded_request) + + # Send the request + response = await AsyncCloudRedisRestTransport._CancelOperation._get_response(self._host, metadata, query_params, self._session, timeout, transcoded_request) + + # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception + # subclass. + if response.status_code >= 400: + content = await response.read() + payload = json.loads(content.decode('utf-8')) + request_url = "{host}{uri}".format(host=self._host, uri=transcoded_request['uri']) + method = transcoded_request['method'] + raise core_exceptions.format_http_response_error(response, method, request_url, payload) # type: ignore + + return await self._interceptor.post_cancel_operation(None) + + @property + def delete_operation(self): + return self._DeleteOperation(self._session, self._host, self._interceptor) # type: ignore + + class _DeleteOperation(_BaseCloudRedisRestTransport._BaseDeleteOperation, AsyncCloudRedisRestStub): + def __hash__(self): + return hash("AsyncCloudRedisRestTransport.DeleteOperation") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None): + + uri = transcoded_request['uri'] + method = transcoded_request['method'] + headers = dict(metadata) + headers['Content-Type'] = 'application/json' + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response + + async def __call__(self, + request: operations_pb2.DeleteOperationRequest, *, + retry: OptionalRetry=gapic_v1.method.DEFAULT, + timeout: Optional[float]=None, + metadata: Sequence[Tuple[str, str]]=(), + ) -> None: + + r"""Call the delete operation method over HTTP. + + Args: + request (operations_pb2.DeleteOperationRequest): + The request object for DeleteOperation method. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + + http_options = _BaseCloudRedisRestTransport._BaseDeleteOperation._get_http_options() + request, metadata = await self._interceptor.pre_delete_operation(request, metadata) + transcoded_request = _BaseCloudRedisRestTransport._BaseDeleteOperation._get_transcoded_request(http_options, request) + + # Jsonify the query params + query_params = _BaseCloudRedisRestTransport._BaseDeleteOperation._get_query_params_json(transcoded_request) + + # Send the request + response = await AsyncCloudRedisRestTransport._DeleteOperation._get_response(self._host, metadata, query_params, self._session, timeout, transcoded_request) + + # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception + # subclass. + if response.status_code >= 400: + content = await response.read() + payload = json.loads(content.decode('utf-8')) + request_url = "{host}{uri}".format(host=self._host, uri=transcoded_request['uri']) + method = transcoded_request['method'] + raise core_exceptions.format_http_response_error(response, method, request_url, payload) # type: ignore + + return await self._interceptor.post_delete_operation(None) + + @property + def get_operation(self): + return self._GetOperation(self._session, self._host, self._interceptor) # type: ignore + + class _GetOperation(_BaseCloudRedisRestTransport._BaseGetOperation, AsyncCloudRedisRestStub): + def __hash__(self): + return hash("AsyncCloudRedisRestTransport.GetOperation") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None): + + uri = transcoded_request['uri'] + method = transcoded_request['method'] + headers = dict(metadata) + headers['Content-Type'] = 'application/json' + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response + + async def __call__(self, + request: operations_pb2.GetOperationRequest, *, + retry: OptionalRetry=gapic_v1.method.DEFAULT, + timeout: Optional[float]=None, + metadata: Sequence[Tuple[str, str]]=(), + ) -> operations_pb2.Operation: + + r"""Call the get operation method over HTTP. + + Args: + request (operations_pb2.GetOperationRequest): + The request object for GetOperation method. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, + should be 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: Response from GetOperation method. + """ + + http_options = _BaseCloudRedisRestTransport._BaseGetOperation._get_http_options() + request, metadata = await self._interceptor.pre_get_operation(request, metadata) + transcoded_request = _BaseCloudRedisRestTransport._BaseGetOperation._get_transcoded_request(http_options, request) + + # Jsonify the query params + query_params = _BaseCloudRedisRestTransport._BaseGetOperation._get_query_params_json(transcoded_request) + + # Send the request + response = await AsyncCloudRedisRestTransport._GetOperation._get_response(self._host, metadata, query_params, self._session, timeout, transcoded_request) + + # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception + # subclass. + if response.status_code >= 400: + content = await response.read() + payload = json.loads(content.decode('utf-8')) + request_url = "{host}{uri}".format(host=self._host, uri=transcoded_request['uri']) + method = transcoded_request['method'] + raise core_exceptions.format_http_response_error(response, method, request_url, payload) # type: ignore + + content = await response.read() + resp = operations_pb2.Operation() + resp = json_format.Parse(content, resp) + resp = await self._interceptor.post_get_operation(resp) + return resp + + @property + def list_operations(self): + return self._ListOperations(self._session, self._host, self._interceptor) # type: ignore + + class _ListOperations(_BaseCloudRedisRestTransport._BaseListOperations, AsyncCloudRedisRestStub): + def __hash__(self): + return hash("AsyncCloudRedisRestTransport.ListOperations") + + @staticmethod + def _get_response( + host, + metadata, + query_params, + session, + timeout, + transcoded_request, + body=None): + + uri = transcoded_request['uri'] + method = transcoded_request['method'] + headers = dict(metadata) + headers['Content-Type'] = 'application/json' + response = getattr(session, method)( + "{host}{uri}".format(host=host, uri=uri), + timeout=timeout, + headers=headers, + params=rest_helpers.flatten_query_params(query_params, strict=True), + ) + return response + + async def __call__(self, + request: operations_pb2.ListOperationsRequest, *, + retry: OptionalRetry=gapic_v1.method.DEFAULT, + timeout: Optional[float]=None, + metadata: Sequence[Tuple[str, str]]=(), + ) -> operations_pb2.ListOperationsResponse: + + r"""Call the list operations method over HTTP. + + Args: + request (operations_pb2.ListOperationsRequest): + The request object for ListOperations method. + retry (google.api_core.retry_async.AsyncRetry): Designation of what errors, if any, + should be 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.ListOperationsResponse: Response from ListOperations method. + """ + + http_options = _BaseCloudRedisRestTransport._BaseListOperations._get_http_options() + request, metadata = await self._interceptor.pre_list_operations(request, metadata) + transcoded_request = _BaseCloudRedisRestTransport._BaseListOperations._get_transcoded_request(http_options, request) + + # Jsonify the query params + query_params = _BaseCloudRedisRestTransport._BaseListOperations._get_query_params_json(transcoded_request) + + # Send the request + response = await AsyncCloudRedisRestTransport._ListOperations._get_response(self._host, metadata, query_params, self._session, timeout, transcoded_request) + + # In case of error, raise the appropriate core_exceptions.GoogleAPICallError exception + # subclass. + if response.status_code >= 400: + content = await response.read() + payload = json.loads(content.decode('utf-8')) + request_url = "{host}{uri}".format(host=self._host, uri=transcoded_request['uri']) + method = transcoded_request['method'] + raise core_exceptions.format_http_response_error(response, method, request_url, payload) # type: ignore + + content = await response.read() + resp = operations_pb2.ListOperationsResponse() + resp = json_format.Parse(content, resp) + resp = await self._interceptor.post_list_operations(resp) + return resp + @property def kind(self) -> str: return "rest_asyncio" diff --git a/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py b/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py index 56b375018..76eea7882 100755 --- a/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py +++ b/tests/integration/goldens/redis/tests/unit/gapic/redis_v1/test_cloud_redis.py @@ -9283,6 +9283,390 @@ async def test_reschedule_maintenance_rest_asyncio_error(): ) +@pytest.mark.asyncio +async def test_get_location_rest_asyncio_bad_request(request_type=locations_pb2.GetLocationRequest): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + request = request_type() + request = json_format.ParseDict({'name': 'projects/sample1/locations/sample2'}, request) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(AsyncAuthorizedSession, 'request') as req, pytest.raises(core_exceptions.BadRequest): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.read = mock.AsyncMock(return_value=b'{}') + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + await client.get_location(request) + +@pytest.mark.asyncio +@pytest.mark.parametrize("request_type", [ + locations_pb2.GetLocationRequest, + dict, +]) +async def test_get_location_rest_asyncio(request_type): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + + request_init = {'name': '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(AsyncAuthorizedSession, 'request') as req: + # Designate an appropriate value for the returned response. + return_value = locations_pb2.Location() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.read = mock.AsyncMock(return_value=json_return_value.encode('UTF-8')) + + req.return_value = response_value + + response = await client.get_location(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, locations_pb2.Location) + +@pytest.mark.asyncio +async def test_list_locations_rest_asyncio_bad_request(request_type=locations_pb2.ListLocationsRequest): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + request = request_type() + request = json_format.ParseDict({'name': 'projects/sample1'}, request) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(AsyncAuthorizedSession, 'request') as req, pytest.raises(core_exceptions.BadRequest): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.read = mock.AsyncMock(return_value=b'{}') + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + await client.list_locations(request) + +@pytest.mark.asyncio +@pytest.mark.parametrize("request_type", [ + locations_pb2.ListLocationsRequest, + dict, +]) +async def test_list_locations_rest_asyncio(request_type): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + + request_init = {'name': 'projects/sample1'} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(AsyncAuthorizedSession, 'request') as req: + # Designate an appropriate value for the returned response. + return_value = locations_pb2.ListLocationsResponse() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.read = mock.AsyncMock(return_value=json_return_value.encode('UTF-8')) + + req.return_value = response_value + + response = await client.list_locations(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, locations_pb2.ListLocationsResponse) + +@pytest.mark.asyncio +async def test_cancel_operation_rest_asyncio_bad_request(request_type=operations_pb2.CancelOperationRequest): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + request = request_type() + request = json_format.ParseDict({'name': 'projects/sample1/locations/sample2/operations/sample3'}, request) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(AsyncAuthorizedSession, 'request') as req, pytest.raises(core_exceptions.BadRequest): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.read = mock.AsyncMock(return_value=b'{}') + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + await client.cancel_operation(request) + +@pytest.mark.asyncio +@pytest.mark.parametrize("request_type", [ + operations_pb2.CancelOperationRequest, + dict, +]) +async def test_cancel_operation_rest_asyncio(request_type): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + + request_init = {'name': 'projects/sample1/locations/sample2/operations/sample3'} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(AsyncAuthorizedSession, 'request') as req: + # Designate an appropriate value for the returned response. + return_value = None + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = '{}' + response_value.read = mock.AsyncMock(return_value=json_return_value.encode('UTF-8')) + + req.return_value = response_value + + response = await client.cancel_operation(request) + + # Establish that the response is the type that we expect. + assert response is None + +@pytest.mark.asyncio +async def test_delete_operation_rest_asyncio_bad_request(request_type=operations_pb2.DeleteOperationRequest): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + request = request_type() + request = json_format.ParseDict({'name': 'projects/sample1/locations/sample2/operations/sample3'}, request) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(AsyncAuthorizedSession, 'request') as req, pytest.raises(core_exceptions.BadRequest): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.read = mock.AsyncMock(return_value=b'{}') + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + await client.delete_operation(request) + +@pytest.mark.asyncio +@pytest.mark.parametrize("request_type", [ + operations_pb2.DeleteOperationRequest, + dict, +]) +async def test_delete_operation_rest_asyncio(request_type): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + + request_init = {'name': 'projects/sample1/locations/sample2/operations/sample3'} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(AsyncAuthorizedSession, 'request') as req: + # Designate an appropriate value for the returned response. + return_value = None + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = '{}' + response_value.read = mock.AsyncMock(return_value=json_return_value.encode('UTF-8')) + + req.return_value = response_value + + response = await client.delete_operation(request) + + # Establish that the response is the type that we expect. + assert response is None + +@pytest.mark.asyncio +async def test_get_operation_rest_asyncio_bad_request(request_type=operations_pb2.GetOperationRequest): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + request = request_type() + request = json_format.ParseDict({'name': 'projects/sample1/locations/sample2/operations/sample3'}, request) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(AsyncAuthorizedSession, 'request') as req, pytest.raises(core_exceptions.BadRequest): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.read = mock.AsyncMock(return_value=b'{}') + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + await client.get_operation(request) + +@pytest.mark.asyncio +@pytest.mark.parametrize("request_type", [ + operations_pb2.GetOperationRequest, + dict, +]) +async def test_get_operation_rest_asyncio(request_type): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + + request_init = {'name': 'projects/sample1/locations/sample2/operations/sample3'} + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(AsyncAuthorizedSession, 'request') as req: + # Designate an appropriate value for the returned response. + return_value = operations_pb2.Operation() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.read = mock.AsyncMock(return_value=json_return_value.encode('UTF-8')) + + req.return_value = response_value + + response = await client.get_operation(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, operations_pb2.Operation) + +@pytest.mark.asyncio +async def test_list_operations_rest_asyncio_bad_request(request_type=operations_pb2.ListOperationsRequest): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + request = request_type() + request = json_format.ParseDict({'name': 'projects/sample1/locations/sample2'}, request) + + # Mock the http request call within the method and fake a BadRequest error. + with mock.patch.object(AsyncAuthorizedSession, 'request') as req, pytest.raises(core_exceptions.BadRequest): + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.read = mock.AsyncMock(return_value=b'{}') + response_value.status_code = 400 + response_value.request = mock.Mock() + req.return_value = response_value + await client.list_operations(request) + +@pytest.mark.asyncio +@pytest.mark.parametrize("request_type", [ + operations_pb2.ListOperationsRequest, + dict, +]) +async def test_list_operations_rest_asyncio(request_type): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") + elif not HAS_AIOHTTP_INSTALLED: + pytest.skip("aiohttp is required for async rest transport.") + elif not HAS_ASYNC_REST_SUPPORT_IN_CORE: + pytest.skip("google-api-core >= 2.20.0 is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio", + ) + + request_init = {'name': '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(AsyncAuthorizedSession, 'request') as req: + # Designate an appropriate value for the returned response. + return_value = operations_pb2.ListOperationsResponse() + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + json_return_value = json_format.MessageToJson(return_value) + response_value.read = mock.AsyncMock(return_value=json_return_value.encode('UTF-8')) + + req.return_value = response_value + + response = await client.list_operations(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, operations_pb2.ListOperationsResponse) + def test_initialize_client_w_rest_asyncio(): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth >= 2.35.0 is required for async rest transport.") diff --git a/tests/system/test_retry.py b/tests/system/test_retry.py index 5b06b127f..6ec707cd5 100644 --- a/tests/system/test_retry.py +++ b/tests/system/test_retry.py @@ -59,8 +59,7 @@ async def test_retry_bubble_async(async_echo): @pytest.mark.asyncio async def test_method_async_wrapper_for_async_client(async_echo): - # TODO(https://github.com/googleapis/gapic-generator-python/issues/2170): Add test for retrying LRO. - with pytest.raises((exceptions.NotFound, NotImplementedError)): + with pytest.raises(exceptions.NotFound): await async_echo.get_operation({ 'name': "operations/echo" })