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 b7b223caad..fe5d69f615 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 @@ -181,7 +181,8 @@ def _get_http_options(): 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) + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2137): Remove `type: ignore` once version check is added for google-api-core. #} + raise core_exceptions.format_http_response_error(response, method, request_url, payload) # type: ignore {% else %} raise core_exceptions.from_http_response(response) {% endif %}{# is_async #} 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 adcedf6299..7ef5f9cb74 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 @@ -16,7 +16,11 @@ from google.auth.aio import credentials as ga_credentials_async # type: ignore 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 +from google.api_core import rest_helpers +from google.protobuf import json_format + +import json # type: ignore import dataclasses from typing import Any, Callable, Tuple, Optional, Sequence, Union @@ -109,16 +113,73 @@ class Async{{service.name}}RestTransport(_Base{{ service.name }}RestTransport): def __hash__(self): return hash("Async{{service.name}}RestTransport.{{method.name}}") + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2168): Implement server streaming method. #} + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2169): Implement client streaming method. #} + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2170): Implement long running operation method. #} + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2171): Implement pager method. #} + {% if method.http_options and not method.client_streaming and not method.server_streaming and not method.lro and not method.extended_lro and not method.paged_result_field %} + {% set body_spec = method.http_options[0].body %} + {{ shared_macros.response_method(body_spec, is_async=True)|indent(8) }} + + {% endif %}{# method.http_options and not method.client_streaming and not method.server_streaming and not method.lro and not method.extended_lro and not method.paged_result_field #} async def __call__(self, request: {{method.input.ident}}, *, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - {# TODO(b/362949446): Update the return type as we implement this for different method types. #} - ) -> None: + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2168): Update return type for server streaming method. #} + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2169): Update return type for client streaming method. #} + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2170): Update the return type for long running operation method. #} + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2171): Update the return type for pager method. #} + ){% if not method.void %} -> {% if not method.server_streaming %}{{method.output.ident}}{% else %}None{% endif %}{% endif %}: + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2168): Implement server streaming method. #} + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2169): Implement client streaming method. #} + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2170): Implement long running operation method. #} + {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2171): Implement pager method. #} + {% if method.http_options and not method.client_streaming and not method.server_streaming and not method.lro and not method.extended_lro and not method.paged_result_field %} + r"""Call the {{- ' ' -}} + {{ (method.name|snake_case).replace('_',' ')|wrap( + width=70, offset=45, indent=8) }} + {{- ' ' -}} method over HTTP. + + Args: + request (~.{{ method.input.ident }}): + The request object.{{ ' ' }} + {{- method.input.meta.doc|rst(width=72, indent=16, nl=False) }} + 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. + {% if not method.void %} + + Returns: + ~.{{ method.output.ident }}: + {{ method.output.meta.doc|rst(width=72, indent=16) }} + {% endif %} + """ + + {{ shared_macros.rest_call_method_common(body_spec, method.name, service.name, is_async=True)|indent(8) }} + + {% if not method.void %} + # Return the response + resp = {{method.output.ident}}() + {% if method.output.ident.is_proto_plus_type %} + pb_resp = {{method.output.ident}}.pb(resp) + {% else %} + pb_resp = resp + {% endif %} + content = await response.read() + json_format.Parse(content, pb_resp, ignore_unknown_fields=True) + return resp + + {% endif %}{# method.void #} + + {% else %} raise NotImplementedError( "Method {{ method.name }} is not available over REST transport" ) + {% endif %}{# method.http_options and not method.client_streaming and not method.server_streaming and not method.lro and not method.extended_lro and not method.paged_result_field #} {% endfor %} {% for method in service.methods.values()|sort(attribute="name") %} 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 a2112c5688..390d0547ac 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 @@ -1051,6 +1051,9 @@ def test_{{ method_name }}_raw_page_lro(): {% with method_name = method.safe_name|snake_case + "_unary" if method.extended_lro and not full_extended_lro else method.name|snake_case, method_output = method.extended_lro.operation_type if method.extended_lro and not full_extended_lro else method.output %}{% if method.http_options %} {# TODO(kbandes): remove this if condition when lro and client streaming are supported. #} {% if not method.client_streaming %} +{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2168): Remove unit test for server streaming method. #} +{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2170): Remove unit test for long running operation method. #} +{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2171): Remove unit test for pager method. #} {# NOTE: This guard is added to avoid generating duplicate tests for methods which are tested elsewhere. As we implement each of the api methods # in the `macro::call_success_test`, the case will be removed from this condition below. # TODO(https://github.com/googleapis/gapic-generator-python/issues/2143): Remove the test `test_{{ method_name }}_rest` from here once the linked issue is resolved. @@ -1940,7 +1943,7 @@ def test_unsupported_parameter_rest_asyncio(): # There are no plans to add support for client streaming. #} {% macro is_rest_unsupported_method(method, is_async) %} -{%- if method.client_streaming or is_async -%} +{%- if method.client_streaming or (is_async and (method.server_streaming or method.lro or method.extended_lro or method.paged_result_field)) -%} {{'True'}} {%- else -%} {{'False'}} @@ -2083,6 +2086,9 @@ def test_initialize_client_w_{{transport_name}}(): # (method.extended_lro and not full_extended_lro) #} {% set method_output = method.output %} +{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2168): Add unit test for server streaming method. #} +{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2170): Add unit test for long running operation method. #} +{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2171): Add unit test for pager method. #} {# TODO(https://github.com/googleapis/gapic-generator-python/issues/2143): Update the guard below as we add support for each method, and keep it in sync with the guard in # `rest_required_tests`, which should be the exact opposite. Remove it once we have all the methods supported in async rest transport that are supported in sync rest transport. #} @@ -2230,10 +2236,10 @@ def test_initialize_client_w_{{transport_name}}(): json_return_value = json_format.MessageToJson(return_value) {% endif %}{# method.void #} {% if is_async %} - response_value.read = mock.AsyncMock(return_value=b'{}') + response_value.read = mock.AsyncMock(return_value=json_return_value.encode('UTF-8')) {% else %}{# is_async #} response_value.content = json_return_value.encode('UTF-8') - {% endif %} + {% endif %}{# is_async #} req.return_value = response_value response = {{ await_prefix }}client.{{ method_name }}(request) diff --git a/noxfile.py b/noxfile.py index 74fb33bc07..2eedbc4403 100644 --- a/noxfile.py +++ b/noxfile.py @@ -444,7 +444,7 @@ def showcase_unit_w_rest_async( # TODO(https://github.com/googleapis/python-api-core/pull/694): Update the version of google-api-core once the linked PR is merged. session.install('--no-cache-dir', '--force-reinstall', "google-api-core[grpc]@git+https://github.com/googleapis/python-api-core.git@7dea20d73878eca93b61bb82ae6ddf335fb3a8ca") # TODO(https://github.com/googleapis/google-auth-library-python/pull/1577): Update the version of google-auth once the linked PR is merged. - session.install('--no-cache-dir', '--force-reinstall', "google-auth@git+https://github.com/googleapis/google-auth-library-python.git@add-support-for-async-authorized-session-api") + session.install('--no-cache-dir', '--force-reinstall', "google-auth@git+https://github.com/googleapis/google-auth-library-python.git") session.install("aiohttp") run_showcase_unit_tests(session) 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 a35870b690..3381a80c6b 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 @@ -23,7 +23,11 @@ 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 +from google.api_core import rest_helpers +from google.protobuf import json_format + +import json # type: ignore import dataclasses from typing import Any, Callable, Tuple, Optional, Sequence, Union @@ -200,7 +204,7 @@ async def __call__(self, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - ) -> None: + ) -> operations_pb2.Operation: raise NotImplementedError( "Method CreateInstance is not available over REST transport" ) @@ -214,7 +218,7 @@ async def __call__(self, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - ) -> None: + ) -> operations_pb2.Operation: raise NotImplementedError( "Method DeleteInstance is not available over REST transport" ) @@ -228,7 +232,7 @@ async def __call__(self, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - ) -> None: + ) -> operations_pb2.Operation: raise NotImplementedError( "Method ExportInstance is not available over REST transport" ) @@ -242,7 +246,7 @@ async def __call__(self, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - ) -> None: + ) -> operations_pb2.Operation: raise NotImplementedError( "Method FailoverInstance is not available over REST transport" ) @@ -251,29 +255,149 @@ class _GetInstance(_BaseCloudRedisRestTransport._BaseGetInstance, AsyncCloudRedi def __hash__(self): return hash("AsyncCloudRedisRestTransport.GetInstance") + @staticmethod + async 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 = await 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: cloud_redis.GetInstanceRequest, *, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - ) -> None: - raise NotImplementedError( - "Method GetInstance is not available over REST transport" - ) + ) -> cloud_redis.Instance: + r"""Call the get instance method over HTTP. + + Args: + request (~.cloud_redis.GetInstanceRequest): + The request object. Request for + [GetInstance][google.cloud.redis.v1.CloudRedis.GetInstance]. + 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: + ~.cloud_redis.Instance: + A Memorystore for Redis instance. + """ + + http_options = _BaseCloudRedisRestTransport._BaseGetInstance._get_http_options() + transcoded_request = _BaseCloudRedisRestTransport._BaseGetInstance._get_transcoded_request(http_options, request) + + # Jsonify the query params + query_params = _BaseCloudRedisRestTransport._BaseGetInstance._get_query_params_json(transcoded_request) + + # Send the request + response = await AsyncCloudRedisRestTransport._GetInstance._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 the response + resp = cloud_redis.Instance() + pb_resp = cloud_redis.Instance.pb(resp) + content = await response.read() + json_format.Parse(content, pb_resp, ignore_unknown_fields=True) + return resp class _GetInstanceAuthString(_BaseCloudRedisRestTransport._BaseGetInstanceAuthString, AsyncCloudRedisRestStub): def __hash__(self): return hash("AsyncCloudRedisRestTransport.GetInstanceAuthString") + @staticmethod + async 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 = await 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: cloud_redis.GetInstanceAuthStringRequest, *, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - ) -> None: - raise NotImplementedError( - "Method GetInstanceAuthString is not available over REST transport" - ) + ) -> cloud_redis.InstanceAuthString: + r"""Call the get instance auth string method over HTTP. + + Args: + request (~.cloud_redis.GetInstanceAuthStringRequest): + The request object. Request for + [GetInstanceAuthString][google.cloud.redis.v1.CloudRedis.GetInstanceAuthString]. + 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: + ~.cloud_redis.InstanceAuthString: + Instance AUTH string details. + """ + + http_options = _BaseCloudRedisRestTransport._BaseGetInstanceAuthString._get_http_options() + transcoded_request = _BaseCloudRedisRestTransport._BaseGetInstanceAuthString._get_transcoded_request(http_options, request) + + # Jsonify the query params + query_params = _BaseCloudRedisRestTransport._BaseGetInstanceAuthString._get_query_params_json(transcoded_request) + + # Send the request + response = await AsyncCloudRedisRestTransport._GetInstanceAuthString._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 the response + resp = cloud_redis.InstanceAuthString() + pb_resp = cloud_redis.InstanceAuthString.pb(resp) + content = await response.read() + json_format.Parse(content, pb_resp, ignore_unknown_fields=True) + return resp class _ImportInstance(_BaseCloudRedisRestTransport._BaseImportInstance, AsyncCloudRedisRestStub): def __hash__(self): @@ -284,7 +408,7 @@ async def __call__(self, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - ) -> None: + ) -> operations_pb2.Operation: raise NotImplementedError( "Method ImportInstance is not available over REST transport" ) @@ -298,7 +422,7 @@ async def __call__(self, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - ) -> None: + ) -> cloud_redis.ListInstancesResponse: raise NotImplementedError( "Method ListInstances is not available over REST transport" ) @@ -312,7 +436,7 @@ async def __call__(self, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - ) -> None: + ) -> operations_pb2.Operation: raise NotImplementedError( "Method RescheduleMaintenance is not available over REST transport" ) @@ -326,7 +450,7 @@ async def __call__(self, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - ) -> None: + ) -> operations_pb2.Operation: raise NotImplementedError( "Method UpdateInstance is not available over REST transport" ) @@ -340,7 +464,7 @@ async def __call__(self, retry: OptionalRetry=gapic_v1.method.DEFAULT, timeout: Optional[float]=None, metadata: Sequence[Tuple[str, str]]=(), - ) -> None: + ) -> operations_pb2.Operation: raise NotImplementedError( "Method UpgradeInstance is not available over REST transport" ) 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 2080273609..609bd19a1a 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 @@ -8421,7 +8421,7 @@ async def test_list_instances_rest_asyncio_error(): @pytest.mark.asyncio -async def test_get_instance_rest_asyncio_error(): +async def test_get_instance_rest_asyncio_bad_request(request_type=cloud_redis.GetInstanceRequest): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") @@ -8429,17 +8429,116 @@ async def test_get_instance_rest_asyncio_error(): credentials=async_anonymous_credentials(), transport="rest_asyncio" ) + # send a request that will satisfy transcoding + request_init = {'name': 'projects/sample1/locations/sample2/instances/sample3'} + request = request_type(**request_init) - with pytest.raises(NotImplementedError) as not_implemented_error: - await client.get_instance({}) - assert ( - "Method GetInstance is not available over REST transport" - in str(not_implemented_error.value) + # 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_instance(request) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("request_type", [ + cloud_redis.GetInstanceRequest, + dict, +]) +async def test_get_instance_rest_asyncio_call_success(request_type): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth > 2.x.x is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio" ) + # send a request that will satisfy transcoding + request_init = {'name': 'projects/sample1/locations/sample2/instances/sample3'} + 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 = cloud_redis.Instance( + name='name_value', + display_name='display_name_value', + location_id='location_id_value', + alternative_location_id='alternative_location_id_value', + redis_version='redis_version_value', + reserved_ip_range='reserved_ip_range_value', + secondary_ip_range='secondary_ip_range_value', + host='host_value', + port=453, + current_location_id='current_location_id_value', + state=cloud_redis.Instance.State.CREATING, + status_message='status_message_value', + tier=cloud_redis.Instance.Tier.BASIC, + memory_size_gb=1499, + authorized_network='authorized_network_value', + persistence_iam_identity='persistence_iam_identity_value', + connect_mode=cloud_redis.Instance.ConnectMode.DIRECT_PEERING, + auth_enabled=True, + transit_encryption_mode=cloud_redis.Instance.TransitEncryptionMode.SERVER_AUTHENTICATION, + replica_count=1384, + read_endpoint='read_endpoint_value', + read_endpoint_port=1920, + read_replicas_mode=cloud_redis.Instance.ReadReplicasMode.READ_REPLICAS_DISABLED, + customer_managed_key='customer_managed_key_value', + suspension_reasons=[cloud_redis.Instance.SuspensionReason.CUSTOMER_MANAGED_KEY_ISSUE], + maintenance_version='maintenance_version_value', + available_maintenance_versions=['available_maintenance_versions_value'], + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = cloud_redis.Instance.pb(return_value) + 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_instance(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, cloud_redis.Instance) + assert response.name == 'name_value' + assert response.display_name == 'display_name_value' + assert response.location_id == 'location_id_value' + assert response.alternative_location_id == 'alternative_location_id_value' + assert response.redis_version == 'redis_version_value' + assert response.reserved_ip_range == 'reserved_ip_range_value' + assert response.secondary_ip_range == 'secondary_ip_range_value' + assert response.host == 'host_value' + assert response.port == 453 + assert response.current_location_id == 'current_location_id_value' + assert response.state == cloud_redis.Instance.State.CREATING + assert response.status_message == 'status_message_value' + assert response.tier == cloud_redis.Instance.Tier.BASIC + assert response.memory_size_gb == 1499 + assert response.authorized_network == 'authorized_network_value' + assert response.persistence_iam_identity == 'persistence_iam_identity_value' + assert response.connect_mode == cloud_redis.Instance.ConnectMode.DIRECT_PEERING + assert response.auth_enabled is True + assert response.transit_encryption_mode == cloud_redis.Instance.TransitEncryptionMode.SERVER_AUTHENTICATION + assert response.replica_count == 1384 + assert response.read_endpoint == 'read_endpoint_value' + assert response.read_endpoint_port == 1920 + assert response.read_replicas_mode == cloud_redis.Instance.ReadReplicasMode.READ_REPLICAS_DISABLED + assert response.customer_managed_key == 'customer_managed_key_value' + assert response.suspension_reasons == [cloud_redis.Instance.SuspensionReason.CUSTOMER_MANAGED_KEY_ISSUE] + assert response.maintenance_version == 'maintenance_version_value' + assert response.available_maintenance_versions == ['available_maintenance_versions_value'] + @pytest.mark.asyncio -async def test_get_instance_auth_string_rest_asyncio_error(): +async def test_get_instance_auth_string_rest_asyncio_bad_request(request_type=cloud_redis.GetInstanceAuthStringRequest): if not HAS_GOOGLE_AUTH_AIO: pytest.skip("google-auth > 2.x.x is required for async rest transport.") @@ -8447,14 +8546,61 @@ async def test_get_instance_auth_string_rest_asyncio_error(): credentials=async_anonymous_credentials(), transport="rest_asyncio" ) + # send a request that will satisfy transcoding + request_init = {'name': 'projects/sample1/locations/sample2/instances/sample3'} + request = request_type(**request_init) - with pytest.raises(NotImplementedError) as not_implemented_error: - await client.get_instance_auth_string({}) - assert ( - "Method GetInstanceAuthString is not available over REST transport" - in str(not_implemented_error.value) + # 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_instance_auth_string(request) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("request_type", [ + cloud_redis.GetInstanceAuthStringRequest, + dict, +]) +async def test_get_instance_auth_string_rest_asyncio_call_success(request_type): + if not HAS_GOOGLE_AUTH_AIO: + pytest.skip("google-auth > 2.x.x is required for async rest transport.") + + client = CloudRedisAsyncClient( + credentials=async_anonymous_credentials(), + transport="rest_asyncio" ) + # send a request that will satisfy transcoding + request_init = {'name': 'projects/sample1/locations/sample2/instances/sample3'} + 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 = cloud_redis.InstanceAuthString( + auth_string='auth_string_value', + ) + + # Wrap the value into a proper Response obj + response_value = mock.Mock() + response_value.status_code = 200 + + # Convert return value to protobuf type + return_value = cloud_redis.InstanceAuthString.pb(return_value) + 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_instance_auth_string(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, cloud_redis.InstanceAuthString) + assert response.auth_string == 'auth_string_value' + @pytest.mark.asyncio async def test_create_instance_rest_asyncio_error():