diff --git a/google/cloud/spanner_admin_database_v1/__init__.py b/google/cloud/spanner_admin_database_v1/__init__.py index 674f0de7a2..d7fddf0236 100644 --- a/google/cloud/spanner_admin_database_v1/__init__.py +++ b/google/cloud/spanner_admin_database_v1/__init__.py @@ -63,6 +63,8 @@ from .types.spanner_database_admin import GetDatabaseDdlRequest from .types.spanner_database_admin import GetDatabaseDdlResponse from .types.spanner_database_admin import GetDatabaseRequest +from .types.spanner_database_admin import InternalUpdateGraphOperationRequest +from .types.spanner_database_admin import InternalUpdateGraphOperationResponse from .types.spanner_database_admin import ListDatabaseOperationsRequest from .types.spanner_database_admin import ListDatabaseOperationsResponse from .types.spanner_database_admin import ListDatabaseRolesRequest @@ -117,6 +119,8 @@ "GetDatabaseDdlResponse", "GetDatabaseRequest", "IncrementalBackupSpec", + "InternalUpdateGraphOperationRequest", + "InternalUpdateGraphOperationResponse", "ListBackupOperationsRequest", "ListBackupOperationsResponse", "ListBackupSchedulesRequest", diff --git a/google/cloud/spanner_admin_database_v1/gapic_metadata.json b/google/cloud/spanner_admin_database_v1/gapic_metadata.json index e5e704ff96..027a4f612b 100644 --- a/google/cloud/spanner_admin_database_v1/gapic_metadata.json +++ b/google/cloud/spanner_admin_database_v1/gapic_metadata.json @@ -75,6 +75,11 @@ "get_iam_policy" ] }, + "InternalUpdateGraphOperation": { + "methods": [ + "internal_update_graph_operation" + ] + }, "ListBackupOperations": { "methods": [ "list_backup_operations" @@ -210,6 +215,11 @@ "get_iam_policy" ] }, + "InternalUpdateGraphOperation": { + "methods": [ + "internal_update_graph_operation" + ] + }, "ListBackupOperations": { "methods": [ "list_backup_operations" @@ -345,6 +355,11 @@ "get_iam_policy" ] }, + "InternalUpdateGraphOperation": { + "methods": [ + "internal_update_graph_operation" + ] + }, "ListBackupOperations": { "methods": [ "list_backup_operations" diff --git a/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py b/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py index 05b090d5a0..41dcf45c48 100644 --- a/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py +++ b/google/cloud/spanner_admin_database_v1/services/database_admin/async_client.py @@ -28,6 +28,7 @@ Type, Union, ) +import uuid from google.cloud.spanner_admin_database_v1 import gapic_version as package_version @@ -3858,6 +3859,126 @@ async def sample_list_backup_schedules(): # Done; return the response. return response + async def internal_update_graph_operation( + self, + request: Optional[ + Union[spanner_database_admin.InternalUpdateGraphOperationRequest, dict] + ] = None, + *, + database: Optional[str] = None, + operation_id: Optional[str] = None, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> spanner_database_admin.InternalUpdateGraphOperationResponse: + r"""This is an internal API called by Spanner Graph jobs. + You should never need to call this API directly. + + .. 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 spanner_admin_database_v1 + + async def sample_internal_update_graph_operation(): + # Create a client + client = spanner_admin_database_v1.DatabaseAdminAsyncClient() + + # Initialize request argument(s) + request = spanner_admin_database_v1.InternalUpdateGraphOperationRequest( + database="database_value", + operation_id="operation_id_value", + vm_identity_token="vm_identity_token_value", + ) + + # Make the request + response = await client.internal_update_graph_operation(request=request) + + # Handle the response + print(response) + + Args: + request (Optional[Union[google.cloud.spanner_admin_database_v1.types.InternalUpdateGraphOperationRequest, dict]]): + The request object. Internal request proto, do not use + directly. + database (:class:`str`): + Internal field, do not use directly. + This corresponds to the ``database`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + operation_id (:class:`str`): + Internal field, do not use directly. + This corresponds to the ``operation_id`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + 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, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + + Returns: + google.cloud.spanner_admin_database_v1.types.InternalUpdateGraphOperationResponse: + Internal response proto, do not use + directly. + + """ + # Create or coerce a protobuf request object. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + flattened_params = [database, operation_id] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance( + request, spanner_database_admin.InternalUpdateGraphOperationRequest + ): + request = spanner_database_admin.InternalUpdateGraphOperationRequest( + request + ) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if database is not None: + request.database = database + if operation_id is not None: + request.operation_id = operation_id + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._client._transport._wrapped_methods[ + self._client._transport.internal_update_graph_operation + ] + + # Validate the universe domain. + self._client._validate_universe_domain() + + # Send the request. + response = await rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Done; return the response. + return response + async def list_operations( self, request: Optional[operations_pb2.ListOperationsRequest] = None, diff --git a/google/cloud/spanner_admin_database_v1/services/database_admin/client.py b/google/cloud/spanner_admin_database_v1/services/database_admin/client.py index 7fc4313641..08211de569 100644 --- a/google/cloud/spanner_admin_database_v1/services/database_admin/client.py +++ b/google/cloud/spanner_admin_database_v1/services/database_admin/client.py @@ -32,6 +32,7 @@ Union, cast, ) +import uuid import warnings from google.cloud.spanner_admin_database_v1 import gapic_version as package_version @@ -4349,6 +4350,125 @@ def sample_list_backup_schedules(): # Done; return the response. return response + def internal_update_graph_operation( + self, + request: Optional[ + Union[spanner_database_admin.InternalUpdateGraphOperationRequest, dict] + ] = None, + *, + database: Optional[str] = None, + operation_id: Optional[str] = None, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> spanner_database_admin.InternalUpdateGraphOperationResponse: + r"""This is an internal API called by Spanner Graph jobs. + You should never need to call this API directly. + + .. 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 spanner_admin_database_v1 + + def sample_internal_update_graph_operation(): + # Create a client + client = spanner_admin_database_v1.DatabaseAdminClient() + + # Initialize request argument(s) + request = spanner_admin_database_v1.InternalUpdateGraphOperationRequest( + database="database_value", + operation_id="operation_id_value", + vm_identity_token="vm_identity_token_value", + ) + + # Make the request + response = client.internal_update_graph_operation(request=request) + + # Handle the response + print(response) + + Args: + request (Union[google.cloud.spanner_admin_database_v1.types.InternalUpdateGraphOperationRequest, dict]): + The request object. Internal request proto, do not use + directly. + database (str): + Internal field, do not use directly. + This corresponds to the ``database`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + operation_id (str): + Internal field, do not use directly. + This corresponds to the ``operation_id`` field + on the ``request`` instance; if ``request`` is provided, this + should not be set. + retry (google.api_core.retry.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + + Returns: + google.cloud.spanner_admin_database_v1.types.InternalUpdateGraphOperationResponse: + Internal response proto, do not use + directly. + + """ + # Create or coerce a protobuf request object. + # - Quick check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + flattened_params = [database, operation_id] + has_flattened_params = ( + len([param for param in flattened_params if param is not None]) > 0 + ) + if request is not None and has_flattened_params: + raise ValueError( + "If the `request` argument is set, then none of " + "the individual field arguments should be set." + ) + + # - Use the request object if provided (there's no risk of modifying the input as + # there are no flattened fields), or create one. + if not isinstance( + request, spanner_database_admin.InternalUpdateGraphOperationRequest + ): + request = spanner_database_admin.InternalUpdateGraphOperationRequest( + request + ) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if database is not None: + request.database = database + if operation_id is not None: + request.operation_id = operation_id + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[ + self._transport.internal_update_graph_operation + ] + + # Validate the universe domain. + self._validate_universe_domain() + + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Done; return the response. + return response + def __enter__(self) -> "DatabaseAdminClient": return self diff --git a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/base.py b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/base.py index c53cc16026..689f6afe96 100644 --- a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/base.py +++ b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/base.py @@ -477,6 +477,11 @@ def _prep_wrapped_messages(self, client_info): default_timeout=3600.0, client_info=client_info, ), + self.internal_update_graph_operation: gapic_v1.method.wrap_method( + self.internal_update_graph_operation, + default_timeout=None, + client_info=client_info, + ), self.cancel_operation: gapic_v1.method.wrap_method( self.cancel_operation, default_timeout=None, @@ -779,6 +784,18 @@ def list_backup_schedules( ]: raise NotImplementedError() + @property + def internal_update_graph_operation( + self, + ) -> Callable[ + [spanner_database_admin.InternalUpdateGraphOperationRequest], + Union[ + spanner_database_admin.InternalUpdateGraphOperationResponse, + Awaitable[spanner_database_admin.InternalUpdateGraphOperationResponse], + ], + ]: + raise NotImplementedError() + @property def list_operations( self, diff --git a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc.py b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc.py index de999d6a71..7d6ce40830 100644 --- a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc.py +++ b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc.py @@ -1222,6 +1222,39 @@ def list_backup_schedules( ) return self._stubs["list_backup_schedules"] + @property + def internal_update_graph_operation( + self, + ) -> Callable[ + [spanner_database_admin.InternalUpdateGraphOperationRequest], + spanner_database_admin.InternalUpdateGraphOperationResponse, + ]: + r"""Return a callable for the internal update graph + operation method over gRPC. + + This is an internal API called by Spanner Graph jobs. + You should never need to call this API directly. + + Returns: + Callable[[~.InternalUpdateGraphOperationRequest], + ~.InternalUpdateGraphOperationResponse]: + 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 "internal_update_graph_operation" not in self._stubs: + self._stubs[ + "internal_update_graph_operation" + ] = self._logged_channel.unary_unary( + "/google.spanner.admin.database.v1.DatabaseAdmin/InternalUpdateGraphOperation", + request_serializer=spanner_database_admin.InternalUpdateGraphOperationRequest.serialize, + response_deserializer=spanner_database_admin.InternalUpdateGraphOperationResponse.deserialize, + ) + return self._stubs["internal_update_graph_operation"] + def close(self): self._logged_channel.close() diff --git a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc_asyncio.py b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc_asyncio.py index b8ea344fbd..72eb10b7b3 100644 --- a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc_asyncio.py +++ b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/grpc_asyncio.py @@ -1247,6 +1247,39 @@ def list_backup_schedules( ) return self._stubs["list_backup_schedules"] + @property + def internal_update_graph_operation( + self, + ) -> Callable[ + [spanner_database_admin.InternalUpdateGraphOperationRequest], + Awaitable[spanner_database_admin.InternalUpdateGraphOperationResponse], + ]: + r"""Return a callable for the internal update graph + operation method over gRPC. + + This is an internal API called by Spanner Graph jobs. + You should never need to call this API directly. + + Returns: + Callable[[~.InternalUpdateGraphOperationRequest], + Awaitable[~.InternalUpdateGraphOperationResponse]]: + 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 "internal_update_graph_operation" not in self._stubs: + self._stubs[ + "internal_update_graph_operation" + ] = self._logged_channel.unary_unary( + "/google.spanner.admin.database.v1.DatabaseAdmin/InternalUpdateGraphOperation", + request_serializer=spanner_database_admin.InternalUpdateGraphOperationRequest.serialize, + response_deserializer=spanner_database_admin.InternalUpdateGraphOperationResponse.deserialize, + ) + return self._stubs["internal_update_graph_operation"] + def _prep_wrapped_messages(self, client_info): """Precompute the wrapped methods, overriding the base class method to use async wrappers.""" self._wrapped_methods = { @@ -1580,6 +1613,11 @@ def _prep_wrapped_messages(self, client_info): default_timeout=3600.0, client_info=client_info, ), + self.internal_update_graph_operation: self._wrap_method( + self.internal_update_graph_operation, + default_timeout=None, + client_info=client_info, + ), self.cancel_operation: self._wrap_method( self.cancel_operation, default_timeout=None, diff --git a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py index efdeb5628a..c144266a1e 100644 --- a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py +++ b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest.py @@ -181,6 +181,14 @@ def post_get_iam_policy(self, response): logging.log(f"Received response: {response}") return response + def pre_internal_update_graph_operation(self, request, metadata): + logging.log(f"Received request: {request}") + return request, metadata + + def post_internal_update_graph_operation(self, response): + logging.log(f"Received response: {response}") + return response + def pre_list_backup_operations(self, request, metadata): logging.log(f"Received request: {request}") return request, metadata @@ -3678,6 +3686,25 @@ def __call__( ) return resp + class _InternalUpdateGraphOperation( + _BaseDatabaseAdminRestTransport._BaseInternalUpdateGraphOperation, + DatabaseAdminRestStub, + ): + def __hash__(self): + return hash("DatabaseAdminRestTransport.InternalUpdateGraphOperation") + + def __call__( + self, + request: spanner_database_admin.InternalUpdateGraphOperationRequest, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Optional[float] = None, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> spanner_database_admin.InternalUpdateGraphOperationResponse: + raise NotImplementedError( + "Method InternalUpdateGraphOperation is not available over REST transport" + ) + class _ListBackupOperations( _BaseDatabaseAdminRestTransport._BaseListBackupOperations, DatabaseAdminRestStub ): @@ -5863,6 +5890,17 @@ def get_iam_policy( # In C++ this would require a dynamic_cast return self._GetIamPolicy(self._session, self._host, self._interceptor) # type: ignore + @property + def internal_update_graph_operation( + self, + ) -> Callable[ + [spanner_database_admin.InternalUpdateGraphOperationRequest], + spanner_database_admin.InternalUpdateGraphOperationResponse, + ]: + # 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._InternalUpdateGraphOperation(self._session, self._host, self._interceptor) # type: ignore + @property def list_backup_operations( self, diff --git a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest_base.py b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest_base.py index 107024f245..d0ee0a2cbb 100644 --- a/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest_base.py +++ b/google/cloud/spanner_admin_database_v1/services/database_admin/transports/rest_base.py @@ -784,6 +784,10 @@ def _get_query_params_json(transcoded_request): query_params["$alt"] = "json;enum-encoding=int" return query_params + class _BaseInternalUpdateGraphOperation: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + class _BaseListBackupOperations: def __hash__(self): # pragma: NO COVER return NotImplementedError("__hash__ must be implemented.") diff --git a/google/cloud/spanner_admin_database_v1/types/__init__.py b/google/cloud/spanner_admin_database_v1/types/__init__.py index e6fde68af0..ca79ddec90 100644 --- a/google/cloud/spanner_admin_database_v1/types/__init__.py +++ b/google/cloud/spanner_admin_database_v1/types/__init__.py @@ -62,6 +62,8 @@ GetDatabaseDdlRequest, GetDatabaseDdlResponse, GetDatabaseRequest, + InternalUpdateGraphOperationRequest, + InternalUpdateGraphOperationResponse, ListDatabaseOperationsRequest, ListDatabaseOperationsResponse, ListDatabaseRolesRequest, @@ -124,6 +126,8 @@ "GetDatabaseDdlRequest", "GetDatabaseDdlResponse", "GetDatabaseRequest", + "InternalUpdateGraphOperationRequest", + "InternalUpdateGraphOperationResponse", "ListDatabaseOperationsRequest", "ListDatabaseOperationsResponse", "ListDatabaseRolesRequest", diff --git a/google/cloud/spanner_admin_database_v1/types/spanner_database_admin.py b/google/cloud/spanner_admin_database_v1/types/spanner_database_admin.py index 8ba9c6cf11..4f60bfc0b9 100644 --- a/google/cloud/spanner_admin_database_v1/types/spanner_database_admin.py +++ b/google/cloud/spanner_admin_database_v1/types/spanner_database_admin.py @@ -25,6 +25,7 @@ from google.protobuf import field_mask_pb2 # type: ignore from google.protobuf import struct_pb2 # type: ignore from google.protobuf import timestamp_pb2 # type: ignore +from google.rpc import status_pb2 # type: ignore __protobuf__ = proto.module( @@ -58,6 +59,8 @@ "AddSplitPointsRequest", "AddSplitPointsResponse", "SplitPoints", + "InternalUpdateGraphOperationRequest", + "InternalUpdateGraphOperationResponse", }, ) @@ -1300,4 +1303,47 @@ class Key(proto.Message): ) +class InternalUpdateGraphOperationRequest(proto.Message): + r"""Internal request proto, do not use directly. + + Attributes: + database (str): + Internal field, do not use directly. + operation_id (str): + Internal field, do not use directly. + vm_identity_token (str): + Internal field, do not use directly. + progress (float): + Internal field, do not use directly. + status (google.rpc.status_pb2.Status): + Internal field, do not use directly. + """ + + database: str = proto.Field( + proto.STRING, + number=1, + ) + operation_id: str = proto.Field( + proto.STRING, + number=2, + ) + vm_identity_token: str = proto.Field( + proto.STRING, + number=5, + ) + progress: float = proto.Field( + proto.DOUBLE, + number=3, + ) + status: status_pb2.Status = proto.Field( + proto.MESSAGE, + number=6, + message=status_pb2.Status, + ) + + +class InternalUpdateGraphOperationResponse(proto.Message): + r"""Internal response proto, do not use directly.""" + + __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py b/google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py index 49de66d0c3..549946f98c 100644 --- a/google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py +++ b/google/cloud/spanner_admin_instance_v1/services/instance_admin/async_client.py @@ -28,6 +28,7 @@ Type, Union, ) +import uuid from google.cloud.spanner_admin_instance_v1 import gapic_version as package_version @@ -52,6 +53,7 @@ from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore from google.longrunning import operations_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore from google.protobuf import field_mask_pb2 # type: ignore from google.protobuf import timestamp_pb2 # type: ignore from .transports.base import InstanceAdminTransport, DEFAULT_CLIENT_INFO @@ -3448,6 +3450,227 @@ async def sample_move_instance(): # Done; return the response. return response + async def list_operations( + self, + request: Optional[operations_pb2.ListOperationsRequest] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> operations_pb2.ListOperationsResponse: + r"""Lists operations that match the specified filter in the request. + + Args: + request (:class:`~.operations_pb2.ListOperationsRequest`): + The request object. Request message 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, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + Returns: + ~.operations_pb2.ListOperationsResponse: + Response message for ``ListOperations`` method. + """ + # Create or coerce a protobuf request object. + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = operations_pb2.ListOperationsRequest(**request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self.transport._wrapped_methods[self._client._transport.list_operations] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Validate the universe domain. + self._client._validate_universe_domain() + + # Send the request. + response = await rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Done; return the response. + return response + + async def get_operation( + self, + request: Optional[operations_pb2.GetOperationRequest] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> operations_pb2.Operation: + r"""Gets the latest state of a long-running operation. + + Args: + request (:class:`~.operations_pb2.GetOperationRequest`): + The request object. Request message 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, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + Returns: + ~.operations_pb2.Operation: + An ``Operation`` object. + """ + # Create or coerce a protobuf request object. + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = operations_pb2.GetOperationRequest(**request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self.transport._wrapped_methods[self._client._transport.get_operation] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Validate the universe domain. + self._client._validate_universe_domain() + + # Send the request. + response = await rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Done; return the response. + return response + + async def delete_operation( + self, + request: Optional[operations_pb2.DeleteOperationRequest] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> None: + r"""Deletes a long-running operation. + + This method indicates that the client is no longer interested + in the operation result. It does not cancel the operation. + If the server doesn't support this method, it returns + `google.rpc.Code.UNIMPLEMENTED`. + + Args: + request (:class:`~.operations_pb2.DeleteOperationRequest`): + The request object. Request message 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, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + Returns: + None + """ + # Create or coerce a protobuf request object. + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = operations_pb2.DeleteOperationRequest(**request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self.transport._wrapped_methods[self._client._transport.delete_operation] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Validate the universe domain. + self._client._validate_universe_domain() + + # Send the request. + await rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + async def cancel_operation( + self, + request: Optional[operations_pb2.CancelOperationRequest] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> None: + r"""Starts asynchronous cancellation on a long-running operation. + + The server makes a best effort to cancel the operation, but success + is not guaranteed. If the server doesn't support this method, it returns + `google.rpc.Code.UNIMPLEMENTED`. + + Args: + request (:class:`~.operations_pb2.CancelOperationRequest`): + The request object. Request message 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, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + Returns: + None + """ + # Create or coerce a protobuf request object. + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = operations_pb2.CancelOperationRequest(**request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self.transport._wrapped_methods[self._client._transport.cancel_operation] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Validate the universe domain. + self._client._validate_universe_domain() + + # Send the request. + await rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + async def __aenter__(self) -> "InstanceAdminAsyncClient": return self diff --git a/google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py b/google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py index 51d7482520..ef34b5361b 100644 --- a/google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py +++ b/google/cloud/spanner_admin_instance_v1/services/instance_admin/client.py @@ -32,6 +32,7 @@ Union, cast, ) +import uuid import warnings from google.cloud.spanner_admin_instance_v1 import gapic_version as package_version @@ -68,6 +69,7 @@ from google.iam.v1 import iam_policy_pb2 # type: ignore from google.iam.v1 import policy_pb2 # type: ignore from google.longrunning import operations_pb2 # type: ignore +from google.longrunning import operations_pb2 # type: ignore from google.protobuf import field_mask_pb2 # type: ignore from google.protobuf import timestamp_pb2 # type: ignore from .transports.base import InstanceAdminTransport, DEFAULT_CLIENT_INFO @@ -3870,6 +3872,235 @@ def __exit__(self, type, value, traceback): """ self.transport.close() + def list_operations( + self, + request: Optional[operations_pb2.ListOperationsRequest] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> operations_pb2.ListOperationsResponse: + r"""Lists operations that match the specified filter in the request. + + Args: + request (:class:`~.operations_pb2.ListOperationsRequest`): + The request object. Request message for + `ListOperations` method. + 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, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + Returns: + ~.operations_pb2.ListOperationsResponse: + Response message for ``ListOperations`` method. + """ + # Create or coerce a protobuf request object. + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = operations_pb2.ListOperationsRequest(**request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.list_operations] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Validate the universe domain. + self._validate_universe_domain() + + try: + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Done; return the response. + return response + except core_exceptions.GoogleAPICallError as e: + self._add_cred_info_for_auth_errors(e) + raise e + + def get_operation( + self, + request: Optional[operations_pb2.GetOperationRequest] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> operations_pb2.Operation: + r"""Gets the latest state of a long-running operation. + + Args: + request (:class:`~.operations_pb2.GetOperationRequest`): + The request object. Request message for + `GetOperation` method. + 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, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + Returns: + ~.operations_pb2.Operation: + An ``Operation`` object. + """ + # Create or coerce a protobuf request object. + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = operations_pb2.GetOperationRequest(**request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_operation] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Validate the universe domain. + self._validate_universe_domain() + + try: + # Send the request. + response = rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + # Done; return the response. + return response + except core_exceptions.GoogleAPICallError as e: + self._add_cred_info_for_auth_errors(e) + raise e + + def delete_operation( + self, + request: Optional[operations_pb2.DeleteOperationRequest] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> None: + r"""Deletes a long-running operation. + + This method indicates that the client is no longer interested + in the operation result. It does not cancel the operation. + If the server doesn't support this method, it returns + `google.rpc.Code.UNIMPLEMENTED`. + + Args: + request (:class:`~.operations_pb2.DeleteOperationRequest`): + The request object. Request message for + `DeleteOperation` method. + 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, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + Returns: + None + """ + # Create or coerce a protobuf request object. + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = operations_pb2.DeleteOperationRequest(**request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.delete_operation] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Validate the universe domain. + self._validate_universe_domain() + + # Send the request. + rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + + def cancel_operation( + self, + request: Optional[operations_pb2.CancelOperationRequest] = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Union[float, object] = gapic_v1.method.DEFAULT, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> None: + r"""Starts asynchronous cancellation on a long-running operation. + + The server makes a best effort to cancel the operation, but success + is not guaranteed. If the server doesn't support this method, it returns + `google.rpc.Code.UNIMPLEMENTED`. + + Args: + request (:class:`~.operations_pb2.CancelOperationRequest`): + The request object. Request message for + `CancelOperation` method. + 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, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + Returns: + None + """ + # Create or coerce a protobuf request object. + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = operations_pb2.CancelOperationRequest(**request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.cancel_operation] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("name", request.name),)), + ) + + # Validate the universe domain. + self._validate_universe_domain() + + # Send the request. + rpc( + request, + retry=retry, + timeout=timeout, + metadata=metadata, + ) + DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( gapic_version=package_version.__version__ diff --git a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/base.py b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/base.py index 3bcd32e6af..5a737b69f7 100644 --- a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/base.py +++ b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/base.py @@ -306,6 +306,26 @@ def _prep_wrapped_messages(self, client_info): default_timeout=None, client_info=client_info, ), + self.cancel_operation: gapic_v1.method.wrap_method( + self.cancel_operation, + default_timeout=None, + client_info=client_info, + ), + self.delete_operation: gapic_v1.method.wrap_method( + self.delete_operation, + default_timeout=None, + client_info=client_info, + ), + self.get_operation: gapic_v1.method.wrap_method( + self.get_operation, + default_timeout=None, + client_info=client_info, + ), + self.list_operations: gapic_v1.method.wrap_method( + self.list_operations, + default_timeout=None, + client_info=client_info, + ), } def close(self): @@ -537,6 +557,39 @@ def move_instance( ]: raise NotImplementedError() + @property + def list_operations( + self, + ) -> Callable[ + [operations_pb2.ListOperationsRequest], + Union[ + operations_pb2.ListOperationsResponse, + Awaitable[operations_pb2.ListOperationsResponse], + ], + ]: + raise NotImplementedError() + + @property + def get_operation( + self, + ) -> Callable[ + [operations_pb2.GetOperationRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + + @property + def cancel_operation( + self, + ) -> Callable[[operations_pb2.CancelOperationRequest], None,]: + raise NotImplementedError() + + @property + def delete_operation( + self, + ) -> Callable[[operations_pb2.DeleteOperationRequest], None,]: + raise NotImplementedError() + @property def kind(self) -> str: raise NotImplementedError() diff --git a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc.py b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc.py index 16ca5cc338..9066da9b07 100644 --- a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc.py +++ b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc.py @@ -1339,6 +1339,76 @@ def move_instance( def close(self): self._logged_channel.close() + @property + def delete_operation( + self, + ) -> Callable[[operations_pb2.DeleteOperationRequest], None]: + r"""Return a callable for the delete_operation method over gRPC.""" + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_operation" not in self._stubs: + self._stubs["delete_operation"] = self._logged_channel.unary_unary( + "/google.longrunning.Operations/DeleteOperation", + request_serializer=operations_pb2.DeleteOperationRequest.SerializeToString, + response_deserializer=None, + ) + return self._stubs["delete_operation"] + + @property + def cancel_operation( + self, + ) -> Callable[[operations_pb2.CancelOperationRequest], None]: + r"""Return a callable for the cancel_operation method over gRPC.""" + # 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 "cancel_operation" not in self._stubs: + self._stubs["cancel_operation"] = self._logged_channel.unary_unary( + "/google.longrunning.Operations/CancelOperation", + request_serializer=operations_pb2.CancelOperationRequest.SerializeToString, + response_deserializer=None, + ) + return self._stubs["cancel_operation"] + + @property + def get_operation( + self, + ) -> Callable[[operations_pb2.GetOperationRequest], operations_pb2.Operation]: + r"""Return a callable for the get_operation method over gRPC.""" + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_operation" not in self._stubs: + self._stubs["get_operation"] = self._logged_channel.unary_unary( + "/google.longrunning.Operations/GetOperation", + request_serializer=operations_pb2.GetOperationRequest.SerializeToString, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["get_operation"] + + @property + def list_operations( + self, + ) -> Callable[ + [operations_pb2.ListOperationsRequest], operations_pb2.ListOperationsResponse + ]: + r"""Return a callable for the list_operations method over gRPC.""" + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_operations" not in self._stubs: + self._stubs["list_operations"] = self._logged_channel.unary_unary( + "/google.longrunning.Operations/ListOperations", + request_serializer=operations_pb2.ListOperationsRequest.SerializeToString, + response_deserializer=operations_pb2.ListOperationsResponse.FromString, + ) + return self._stubs["list_operations"] + @property def kind(self) -> str: return "grpc" diff --git a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc_asyncio.py b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc_asyncio.py index b28b9d1ed4..04793a6bc3 100644 --- a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc_asyncio.py +++ b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/grpc_asyncio.py @@ -1521,6 +1521,26 @@ def _prep_wrapped_messages(self, client_info): default_timeout=None, client_info=client_info, ), + self.cancel_operation: self._wrap_method( + self.cancel_operation, + default_timeout=None, + client_info=client_info, + ), + self.delete_operation: self._wrap_method( + self.delete_operation, + default_timeout=None, + client_info=client_info, + ), + self.get_operation: self._wrap_method( + self.get_operation, + default_timeout=None, + client_info=client_info, + ), + self.list_operations: self._wrap_method( + self.list_operations, + default_timeout=None, + client_info=client_info, + ), } def _wrap_method(self, func, *args, **kwargs): @@ -1535,5 +1555,75 @@ def close(self): def kind(self) -> str: return "grpc_asyncio" + @property + def delete_operation( + self, + ) -> Callable[[operations_pb2.DeleteOperationRequest], None]: + r"""Return a callable for the delete_operation method over gRPC.""" + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "delete_operation" not in self._stubs: + self._stubs["delete_operation"] = self._logged_channel.unary_unary( + "/google.longrunning.Operations/DeleteOperation", + request_serializer=operations_pb2.DeleteOperationRequest.SerializeToString, + response_deserializer=None, + ) + return self._stubs["delete_operation"] + + @property + def cancel_operation( + self, + ) -> Callable[[operations_pb2.CancelOperationRequest], None]: + r"""Return a callable for the cancel_operation method over gRPC.""" + # 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 "cancel_operation" not in self._stubs: + self._stubs["cancel_operation"] = self._logged_channel.unary_unary( + "/google.longrunning.Operations/CancelOperation", + request_serializer=operations_pb2.CancelOperationRequest.SerializeToString, + response_deserializer=None, + ) + return self._stubs["cancel_operation"] + + @property + def get_operation( + self, + ) -> Callable[[operations_pb2.GetOperationRequest], operations_pb2.Operation]: + r"""Return a callable for the get_operation method over gRPC.""" + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_operation" not in self._stubs: + self._stubs["get_operation"] = self._logged_channel.unary_unary( + "/google.longrunning.Operations/GetOperation", + request_serializer=operations_pb2.GetOperationRequest.SerializeToString, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["get_operation"] + + @property + def list_operations( + self, + ) -> Callable[ + [operations_pb2.ListOperationsRequest], operations_pb2.ListOperationsResponse + ]: + r"""Return a callable for the list_operations method over gRPC.""" + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "list_operations" not in self._stubs: + self._stubs["list_operations"] = self._logged_channel.unary_unary( + "/google.longrunning.Operations/ListOperations", + request_serializer=operations_pb2.ListOperationsRequest.SerializeToString, + response_deserializer=operations_pb2.ListOperationsResponse.FromString, + ) + return self._stubs["list_operations"] + __all__ = ("InstanceAdminGrpcAsyncIOTransport",) diff --git a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py index 571e303bfc..ca32cafa99 100644 --- a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py +++ b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest.py @@ -1191,6 +1191,102 @@ def post_update_instance_partition_with_metadata( """ return response, metadata + def pre_cancel_operation( + self, + request: operations_pb2.CancelOperationRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + operations_pb2.CancelOperationRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: + """Pre-rpc interceptor for cancel_operation + + Override in a subclass to manipulate the request or metadata + before they are sent to the InstanceAdmin server. + """ + return request, metadata + + 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 InstanceAdmin server but before + it is returned to user code. + """ + return response + + def pre_delete_operation( + self, + request: operations_pb2.DeleteOperationRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + operations_pb2.DeleteOperationRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: + """Pre-rpc interceptor for delete_operation + + Override in a subclass to manipulate the request or metadata + before they are sent to the InstanceAdmin server. + """ + return request, metadata + + 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 InstanceAdmin server but before + it is returned to user code. + """ + return response + + def pre_get_operation( + self, + request: operations_pb2.GetOperationRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + operations_pb2.GetOperationRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: + """Pre-rpc interceptor for get_operation + + Override in a subclass to manipulate the request or metadata + before they are sent to the InstanceAdmin server. + """ + return request, metadata + + 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 InstanceAdmin server but before + it is returned to user code. + """ + return response + + def pre_list_operations( + self, + request: operations_pb2.ListOperationsRequest, + metadata: Sequence[Tuple[str, Union[str, bytes]]], + ) -> Tuple[ + operations_pb2.ListOperationsRequest, Sequence[Tuple[str, Union[str, bytes]]] + ]: + """Pre-rpc interceptor for list_operations + + Override in a subclass to manipulate the request or metadata + before they are sent to the InstanceAdmin server. + """ + return request, metadata + + 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 InstanceAdmin server but before + it is returned to user code. + """ + return response + @dataclasses.dataclass class InstanceAdminRestStub: @@ -1311,6 +1407,58 @@ def operations_client(self) -> operations_v1.AbstractOperationsClient: # Only create a new client if we do not already have one. if self._operations_client is None: http_options: Dict[str, List[Dict[str, str]]] = { + "google.longrunning.Operations.CancelOperation": [ + { + "method": "post", + "uri": "/v1/{name=projects/*/instances/*/databases/*/operations/*}:cancel", + }, + { + "method": "post", + "uri": "/v1/{name=projects/*/instances/*/operations/*}:cancel", + }, + { + "method": "post", + "uri": "/v1/{name=projects/*/instances/*/backups/*/operations/*}:cancel", + }, + { + "method": "post", + "uri": "/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}:cancel", + }, + { + "method": "post", + "uri": "/v1/{name=projects/*/instanceConfigs/*/operations/*}:cancel", + }, + { + "method": "post", + "uri": "/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}:cancel", + }, + ], + "google.longrunning.Operations.DeleteOperation": [ + { + "method": "delete", + "uri": "/v1/{name=projects/*/instances/*/databases/*/operations/*}", + }, + { + "method": "delete", + "uri": "/v1/{name=projects/*/instances/*/operations/*}", + }, + { + "method": "delete", + "uri": "/v1/{name=projects/*/instances/*/backups/*/operations/*}", + }, + { + "method": "delete", + "uri": "/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}", + }, + { + "method": "delete", + "uri": "/v1/{name=projects/*/instanceConfigs/*/operations/*}", + }, + { + "method": "delete", + "uri": "/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}", + }, + ], "google.longrunning.Operations.GetOperation": [ { "method": "get", @@ -1320,6 +1468,22 @@ def operations_client(self) -> operations_v1.AbstractOperationsClient: "method": "get", "uri": "/v1/{name=projects/*/instances/*/operations/*}", }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/backups/*/operations/*}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instanceConfigs/*/operations/*}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}", + }, ], "google.longrunning.Operations.ListOperations": [ { @@ -1330,25 +1494,21 @@ def operations_client(self) -> operations_v1.AbstractOperationsClient: "method": "get", "uri": "/v1/{name=projects/*/instances/*/operations}", }, - ], - "google.longrunning.Operations.CancelOperation": [ { - "method": "post", - "uri": "/v1/{name=projects/*/instances/*/databases/*/operations/*}:cancel", + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/backups/*/operations}", }, { - "method": "post", - "uri": "/v1/{name=projects/*/instances/*/operations/*}:cancel", + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/instancePartitions/*/operations}", }, - ], - "google.longrunning.Operations.DeleteOperation": [ { - "method": "delete", - "uri": "/v1/{name=projects/*/instances/*/databases/*/operations/*}", + "method": "get", + "uri": "/v1/{name=projects/*/instanceConfigs/*/operations}", }, { - "method": "delete", - "uri": "/v1/{name=projects/*/instances/*/operations/*}", + "method": "get", + "uri": "/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations}", }, ], } @@ -4823,6 +4983,514 @@ def update_instance_partition( # In C++ this would require a dynamic_cast return self._UpdateInstancePartition(self._session, self._host, self._interceptor) # type: ignore + @property + def cancel_operation(self): + return self._CancelOperation(self._session, self._host, self._interceptor) # type: ignore + + class _CancelOperation( + _BaseInstanceAdminRestTransport._BaseCancelOperation, InstanceAdminRestStub + ): + def __hash__(self): + return hash("InstanceAdminRestTransport.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 + + def __call__( + self, + request: operations_pb2.CancelOperationRequest, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Optional[float] = None, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> 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.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + """ + + http_options = ( + _BaseInstanceAdminRestTransport._BaseCancelOperation._get_http_options() + ) + + request, metadata = self._interceptor.pre_cancel_operation( + request, metadata + ) + transcoded_request = _BaseInstanceAdminRestTransport._BaseCancelOperation._get_transcoded_request( + http_options, request + ) + + # Jsonify the query params + query_params = _BaseInstanceAdminRestTransport._BaseCancelOperation._get_query_params_json( + transcoded_request + ) + + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.spanner.admin.instance_v1.InstanceAdminClient.CancelOperation", + extra={ + "serviceName": "google.spanner.admin.instance.v1.InstanceAdmin", + "rpcName": "CancelOperation", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + + # Send the request + response = InstanceAdminRestTransport._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: + raise core_exceptions.from_http_response(response) + + return self._interceptor.post_cancel_operation(None) + + @property + def delete_operation(self): + return self._DeleteOperation(self._session, self._host, self._interceptor) # type: ignore + + class _DeleteOperation( + _BaseInstanceAdminRestTransport._BaseDeleteOperation, InstanceAdminRestStub + ): + def __hash__(self): + return hash("InstanceAdminRestTransport.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 + + def __call__( + self, + request: operations_pb2.DeleteOperationRequest, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Optional[float] = None, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> 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.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + """ + + http_options = ( + _BaseInstanceAdminRestTransport._BaseDeleteOperation._get_http_options() + ) + + request, metadata = self._interceptor.pre_delete_operation( + request, metadata + ) + transcoded_request = _BaseInstanceAdminRestTransport._BaseDeleteOperation._get_transcoded_request( + http_options, request + ) + + # Jsonify the query params + query_params = _BaseInstanceAdminRestTransport._BaseDeleteOperation._get_query_params_json( + transcoded_request + ) + + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.spanner.admin.instance_v1.InstanceAdminClient.DeleteOperation", + extra={ + "serviceName": "google.spanner.admin.instance.v1.InstanceAdmin", + "rpcName": "DeleteOperation", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + + # Send the request + response = InstanceAdminRestTransport._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: + raise core_exceptions.from_http_response(response) + + return self._interceptor.post_delete_operation(None) + + @property + def get_operation(self): + return self._GetOperation(self._session, self._host, self._interceptor) # type: ignore + + class _GetOperation( + _BaseInstanceAdminRestTransport._BaseGetOperation, InstanceAdminRestStub + ): + def __hash__(self): + return hash("InstanceAdminRestTransport.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 + + def __call__( + self, + request: operations_pb2.GetOperationRequest, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Optional[float] = None, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> 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.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + + Returns: + operations_pb2.Operation: Response from GetOperation method. + """ + + http_options = ( + _BaseInstanceAdminRestTransport._BaseGetOperation._get_http_options() + ) + + request, metadata = self._interceptor.pre_get_operation(request, metadata) + transcoded_request = _BaseInstanceAdminRestTransport._BaseGetOperation._get_transcoded_request( + http_options, request + ) + + # Jsonify the query params + query_params = _BaseInstanceAdminRestTransport._BaseGetOperation._get_query_params_json( + transcoded_request + ) + + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.spanner.admin.instance_v1.InstanceAdminClient.GetOperation", + extra={ + "serviceName": "google.spanner.admin.instance.v1.InstanceAdmin", + "rpcName": "GetOperation", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + + # Send the request + response = InstanceAdminRestTransport._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: + raise core_exceptions.from_http_response(response) + + content = response.content.decode("utf-8") + resp = operations_pb2.Operation() + resp = json_format.Parse(content, resp) + resp = self._interceptor.post_get_operation(resp) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = json_format.MessageToJson(resp) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.spanner.admin.instance_v1.InstanceAdminAsyncClient.GetOperation", + extra={ + "serviceName": "google.spanner.admin.instance.v1.InstanceAdmin", + "rpcName": "GetOperation", + "httpResponse": http_response, + "metadata": http_response["headers"], + }, + ) + return resp + + @property + def list_operations(self): + return self._ListOperations(self._session, self._host, self._interceptor) # type: ignore + + class _ListOperations( + _BaseInstanceAdminRestTransport._BaseListOperations, InstanceAdminRestStub + ): + def __hash__(self): + return hash("InstanceAdminRestTransport.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 + + def __call__( + self, + request: operations_pb2.ListOperationsRequest, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: Optional[float] = None, + metadata: Sequence[Tuple[str, Union[str, bytes]]] = (), + ) -> 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.Retry): Designation of what errors, if any, + should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, Union[str, bytes]]]): Key/value pairs which should be + sent along with the request as metadata. Normally, each value must be of type `str`, + but for metadata keys ending with the suffix `-bin`, the corresponding values must + be of type `bytes`. + + Returns: + operations_pb2.ListOperationsResponse: Response from ListOperations method. + """ + + http_options = ( + _BaseInstanceAdminRestTransport._BaseListOperations._get_http_options() + ) + + request, metadata = self._interceptor.pre_list_operations(request, metadata) + transcoded_request = _BaseInstanceAdminRestTransport._BaseListOperations._get_transcoded_request( + http_options, request + ) + + # Jsonify the query params + query_params = _BaseInstanceAdminRestTransport._BaseListOperations._get_query_params_json( + transcoded_request + ) + + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + request_url = "{host}{uri}".format( + host=self._host, uri=transcoded_request["uri"] + ) + method = transcoded_request["method"] + try: + request_payload = json_format.MessageToJson(request) + except: + request_payload = None + http_request = { + "payload": request_payload, + "requestMethod": method, + "requestUrl": request_url, + "headers": dict(metadata), + } + _LOGGER.debug( + f"Sending request for google.spanner.admin.instance_v1.InstanceAdminClient.ListOperations", + extra={ + "serviceName": "google.spanner.admin.instance.v1.InstanceAdmin", + "rpcName": "ListOperations", + "httpRequest": http_request, + "metadata": http_request["headers"], + }, + ) + + # Send the request + response = InstanceAdminRestTransport._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: + raise core_exceptions.from_http_response(response) + + content = response.content.decode("utf-8") + resp = operations_pb2.ListOperationsResponse() + resp = json_format.Parse(content, resp) + resp = self._interceptor.post_list_operations(resp) + if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor( + logging.DEBUG + ): # pragma: NO COVER + try: + response_payload = json_format.MessageToJson(resp) + except: + response_payload = None + http_response = { + "payload": response_payload, + "headers": dict(response.headers), + "status": response.status_code, + } + _LOGGER.debug( + "Received response for google.spanner.admin.instance_v1.InstanceAdminAsyncClient.ListOperations", + extra={ + "serviceName": "google.spanner.admin.instance.v1.InstanceAdmin", + "rpcName": "ListOperations", + "httpResponse": http_response, + "metadata": http_response["headers"], + }, + ) + return resp + @property def kind(self) -> str: return "rest" diff --git a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest_base.py b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest_base.py index 906fb7b224..bf41644213 100644 --- a/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest_base.py +++ b/google/cloud/spanner_admin_instance_v1/services/instance_admin/transports/rest_base.py @@ -1194,5 +1194,185 @@ def _get_query_params_json(transcoded_request): query_params["$alt"] = "json;enum-encoding=int" return query_params + class _BaseCancelOperation: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "post", + "uri": "/v1/{name=projects/*/instances/*/databases/*/operations/*}:cancel", + }, + { + "method": "post", + "uri": "/v1/{name=projects/*/instances/*/operations/*}:cancel", + }, + { + "method": "post", + "uri": "/v1/{name=projects/*/instances/*/backups/*/operations/*}:cancel", + }, + { + "method": "post", + "uri": "/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}:cancel", + }, + { + "method": "post", + "uri": "/v1/{name=projects/*/instanceConfigs/*/operations/*}:cancel", + }, + { + "method": "post", + "uri": "/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}:cancel", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + class _BaseDeleteOperation: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "delete", + "uri": "/v1/{name=projects/*/instances/*/databases/*/operations/*}", + }, + { + "method": "delete", + "uri": "/v1/{name=projects/*/instances/*/operations/*}", + }, + { + "method": "delete", + "uri": "/v1/{name=projects/*/instances/*/backups/*/operations/*}", + }, + { + "method": "delete", + "uri": "/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}", + }, + { + "method": "delete", + "uri": "/v1/{name=projects/*/instanceConfigs/*/operations/*}", + }, + { + "method": "delete", + "uri": "/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + class _BaseGetOperation: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/databases/*/operations/*}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/operations/*}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/backups/*/operations/*}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/instancePartitions/*/operations/*}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instanceConfigs/*/operations/*}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations/*}", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + + class _BaseListOperations: + def __hash__(self): # pragma: NO COVER + return NotImplementedError("__hash__ must be implemented.") + + @staticmethod + def _get_http_options(): + http_options: List[Dict[str, str]] = [ + { + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/databases/*/operations}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/operations}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/backups/*/operations}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instances/*/instancePartitions/*/operations}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instanceConfigs/*/operations}", + }, + { + "method": "get", + "uri": "/v1/{name=projects/*/instanceConfigs/*/ssdCaches/*/operations}", + }, + ] + return http_options + + @staticmethod + def _get_transcoded_request(http_options, request): + request_kwargs = json_format.MessageToDict(request) + transcoded_request = path_template.transcode(http_options, **request_kwargs) + return transcoded_request + + @staticmethod + def _get_query_params_json(transcoded_request): + query_params = json.loads(json.dumps(transcoded_request["query_params"])) + return query_params + __all__ = ("_BaseInstanceAdminRestTransport",) diff --git a/google/cloud/spanner_v1/types/__init__.py b/google/cloud/spanner_v1/types/__init__.py index afb030c504..e2f87d65da 100644 --- a/google/cloud/spanner_v1/types/__init__.py +++ b/google/cloud/spanner_v1/types/__init__.py @@ -13,6 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # +from .change_stream import ( + ChangeStreamRecord, +) from .commit_response import ( CommitResponse, ) @@ -73,6 +76,7 @@ ) __all__ = ( + "ChangeStreamRecord", "CommitResponse", "KeyRange", "KeySet", diff --git a/google/cloud/spanner_v1/types/change_stream.py b/google/cloud/spanner_v1/types/change_stream.py new file mode 100644 index 0000000000..fb88824c19 --- /dev/null +++ b/google/cloud/spanner_v1/types/change_stream.py @@ -0,0 +1,700 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +from __future__ import annotations + +from typing import MutableMapping, MutableSequence + +import proto # type: ignore + +from google.cloud.spanner_v1.types import type as gs_type +from google.protobuf import struct_pb2 # type: ignore +from google.protobuf import timestamp_pb2 # type: ignore + + +__protobuf__ = proto.module( + package="google.spanner.v1", + manifest={ + "ChangeStreamRecord", + }, +) + + +class ChangeStreamRecord(proto.Message): + r"""Spanner Change Streams enable customers to capture and stream out + changes to their Spanner databases in real-time. A change stream can + be created with option partition_mode='IMMUTABLE_KEY_RANGE' or + partition_mode='MUTABLE_KEY_RANGE'. + + This message is only used in Change Streams created with the option + partition_mode='MUTABLE_KEY_RANGE'. Spanner automatically creates a + special Table-Valued Function (TVF) along with each Change Streams. + The function provides access to the change stream's records. The + function is named READ_ (where + is the name of the change stream), and it + returns a table with only one column called ChangeRecord. + + This message has `oneof`_ fields (mutually exclusive fields). + For each oneof, at most one member field can be set at the same time. + Setting any member of the oneof automatically clears all other + members. + + .. _oneof: https://proto-plus-python.readthedocs.io/en/stable/fields.html#oneofs-mutually-exclusive-fields + + Attributes: + data_change_record (google.cloud.spanner_v1.types.ChangeStreamRecord.DataChangeRecord): + Data change record describing a data change + for a change stream partition. + + This field is a member of `oneof`_ ``record``. + heartbeat_record (google.cloud.spanner_v1.types.ChangeStreamRecord.HeartbeatRecord): + Heartbeat record describing a heartbeat for a + change stream partition. + + This field is a member of `oneof`_ ``record``. + partition_start_record (google.cloud.spanner_v1.types.ChangeStreamRecord.PartitionStartRecord): + Partition start record describing a new + change stream partition. + + This field is a member of `oneof`_ ``record``. + partition_end_record (google.cloud.spanner_v1.types.ChangeStreamRecord.PartitionEndRecord): + Partition end record describing a terminated + change stream partition. + + This field is a member of `oneof`_ ``record``. + partition_event_record (google.cloud.spanner_v1.types.ChangeStreamRecord.PartitionEventRecord): + Partition event record describing key range + changes for a change stream partition. + + This field is a member of `oneof`_ ``record``. + """ + + class DataChangeRecord(proto.Message): + r"""A data change record contains a set of changes to a table + with the same modification type (insert, update, or delete) + committed at the same commit timestamp in one change stream + partition for the same transaction. Multiple data change records + can be returned for the same transaction across multiple change + stream partitions. + + Attributes: + commit_timestamp (google.protobuf.timestamp_pb2.Timestamp): + Indicates the timestamp in which the change was committed. + DataChangeRecord.commit_timestamps, + PartitionStartRecord.start_timestamps, + PartitionEventRecord.commit_timestamps, and + PartitionEndRecord.end_timestamps can have the same value in + the same partition. + record_sequence (str): + Record sequence numbers are unique and monotonically + increasing (but not necessarily contiguous) for a specific + timestamp across record types in the same partition. To + guarantee ordered processing, the reader should process + records (of potentially different types) in record_sequence + order for a specific timestamp in the same partition. + + The record sequence number ordering across partitions is + only meaningful in the context of a specific transaction. + Record sequence numbers are unique across partitions for a + specific transaction. Sort the DataChangeRecords for the + same + [server_transaction_id][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.server_transaction_id] + by + [record_sequence][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.record_sequence] + to reconstruct the ordering of the changes within the + transaction. + server_transaction_id (str): + Provides a globally unique string that represents the + transaction in which the change was committed. Multiple + transactions can have the same commit timestamp, but each + transaction has a unique server_transaction_id. + is_last_record_in_transaction_in_partition (bool): + Indicates whether this is the last record for + a transaction in the current partition. Clients + can use this field to determine when all + records for a transaction in the current + partition have been received. + table (str): + Name of the table affected by the change. + column_metadata (MutableSequence[google.cloud.spanner_v1.types.ChangeStreamRecord.DataChangeRecord.ColumnMetadata]): + Provides metadata describing the columns associated with the + [mods][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.mods] + listed below. + mods (MutableSequence[google.cloud.spanner_v1.types.ChangeStreamRecord.DataChangeRecord.Mod]): + Describes the changes that were made. + mod_type (google.cloud.spanner_v1.types.ChangeStreamRecord.DataChangeRecord.ModType): + Describes the type of change. + value_capture_type (google.cloud.spanner_v1.types.ChangeStreamRecord.DataChangeRecord.ValueCaptureType): + Describes the value capture type that was + specified in the change stream configuration + when this change was captured. + number_of_records_in_transaction (int): + Indicates the number of data change records + that are part of this transaction across all + change stream partitions. This value can be used + to assemble all the records associated with a + particular transaction. + number_of_partitions_in_transaction (int): + Indicates the number of partitions that + return data change records for this transaction. + This value can be helpful in assembling all + records associated with a particular + transaction. + transaction_tag (str): + Indicates the transaction tag associated with + this transaction. + is_system_transaction (bool): + Indicates whether the transaction is a system + transaction. System transactions include those + issued by time-to-live (TTL), column backfill, + etc. + """ + + class ModType(proto.Enum): + r"""Mod type describes the type of change Spanner applied to the data. + For example, if the client submits an INSERT_OR_UPDATE request, + Spanner will perform an insert if there is no existing row and + return ModType INSERT. Alternatively, if there is an existing row, + Spanner will perform an update and return ModType UPDATE. + + Values: + MOD_TYPE_UNSPECIFIED (0): + Not specified. + INSERT (10): + Indicates data was inserted. + UPDATE (20): + Indicates existing data was updated. + DELETE (30): + Indicates existing data was deleted. + """ + MOD_TYPE_UNSPECIFIED = 0 + INSERT = 10 + UPDATE = 20 + DELETE = 30 + + class ValueCaptureType(proto.Enum): + r"""Value capture type describes which values are recorded in the + data change record. + + Values: + VALUE_CAPTURE_TYPE_UNSPECIFIED (0): + Not specified. + OLD_AND_NEW_VALUES (10): + Records both old and new values of the + modified watched columns. + NEW_VALUES (20): + Records only new values of the modified + watched columns. + NEW_ROW (30): + Records new values of all watched columns, + including modified and unmodified columns. + NEW_ROW_AND_OLD_VALUES (40): + Records the new values of all watched + columns, including modified and unmodified + columns. Also records the old values of the + modified columns. + """ + VALUE_CAPTURE_TYPE_UNSPECIFIED = 0 + OLD_AND_NEW_VALUES = 10 + NEW_VALUES = 20 + NEW_ROW = 30 + NEW_ROW_AND_OLD_VALUES = 40 + + class ColumnMetadata(proto.Message): + r"""Metadata for a column. + + Attributes: + name (str): + Name of the column. + type_ (google.cloud.spanner_v1.types.Type): + Type of the column. + is_primary_key (bool): + Indicates whether the column is a primary key + column. + ordinal_position (int): + Ordinal position of the column based on the + original table definition in the schema starting + with a value of 1. + """ + + name: str = proto.Field( + proto.STRING, + number=1, + ) + type_: gs_type.Type = proto.Field( + proto.MESSAGE, + number=2, + message=gs_type.Type, + ) + is_primary_key: bool = proto.Field( + proto.BOOL, + number=3, + ) + ordinal_position: int = proto.Field( + proto.INT64, + number=4, + ) + + class ModValue(proto.Message): + r"""Returns the value and associated metadata for a particular field of + the + [Mod][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.Mod]. + + Attributes: + column_metadata_index (int): + Index within the repeated + [column_metadata][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.column_metadata] + field, to obtain the column metadata for the column that was + modified. + value (google.protobuf.struct_pb2.Value): + The value of the column. + """ + + column_metadata_index: int = proto.Field( + proto.INT32, + number=1, + ) + value: struct_pb2.Value = proto.Field( + proto.MESSAGE, + number=2, + message=struct_pb2.Value, + ) + + class Mod(proto.Message): + r"""A mod describes all data changes in a watched table row. + + Attributes: + keys (MutableSequence[google.cloud.spanner_v1.types.ChangeStreamRecord.DataChangeRecord.ModValue]): + Returns the value of the primary key of the + modified row. + old_values (MutableSequence[google.cloud.spanner_v1.types.ChangeStreamRecord.DataChangeRecord.ModValue]): + Returns the old values before the change for the modified + columns. Always empty for + [INSERT][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.ModType.INSERT], + or if old values are not being captured specified by + [value_capture_type][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.ValueCaptureType]. + new_values (MutableSequence[google.cloud.spanner_v1.types.ChangeStreamRecord.DataChangeRecord.ModValue]): + Returns the new values after the change for the modified + columns. Always empty for + [DELETE][google.spanner.v1.ChangeStreamRecord.DataChangeRecord.ModType.DELETE]. + """ + + keys: MutableSequence[ + "ChangeStreamRecord.DataChangeRecord.ModValue" + ] = proto.RepeatedField( + proto.MESSAGE, + number=1, + message="ChangeStreamRecord.DataChangeRecord.ModValue", + ) + old_values: MutableSequence[ + "ChangeStreamRecord.DataChangeRecord.ModValue" + ] = proto.RepeatedField( + proto.MESSAGE, + number=2, + message="ChangeStreamRecord.DataChangeRecord.ModValue", + ) + new_values: MutableSequence[ + "ChangeStreamRecord.DataChangeRecord.ModValue" + ] = proto.RepeatedField( + proto.MESSAGE, + number=3, + message="ChangeStreamRecord.DataChangeRecord.ModValue", + ) + + commit_timestamp: timestamp_pb2.Timestamp = proto.Field( + proto.MESSAGE, + number=1, + message=timestamp_pb2.Timestamp, + ) + record_sequence: str = proto.Field( + proto.STRING, + number=2, + ) + server_transaction_id: str = proto.Field( + proto.STRING, + number=3, + ) + is_last_record_in_transaction_in_partition: bool = proto.Field( + proto.BOOL, + number=4, + ) + table: str = proto.Field( + proto.STRING, + number=5, + ) + column_metadata: MutableSequence[ + "ChangeStreamRecord.DataChangeRecord.ColumnMetadata" + ] = proto.RepeatedField( + proto.MESSAGE, + number=6, + message="ChangeStreamRecord.DataChangeRecord.ColumnMetadata", + ) + mods: MutableSequence[ + "ChangeStreamRecord.DataChangeRecord.Mod" + ] = proto.RepeatedField( + proto.MESSAGE, + number=7, + message="ChangeStreamRecord.DataChangeRecord.Mod", + ) + mod_type: "ChangeStreamRecord.DataChangeRecord.ModType" = proto.Field( + proto.ENUM, + number=8, + enum="ChangeStreamRecord.DataChangeRecord.ModType", + ) + value_capture_type: "ChangeStreamRecord.DataChangeRecord.ValueCaptureType" = ( + proto.Field( + proto.ENUM, + number=9, + enum="ChangeStreamRecord.DataChangeRecord.ValueCaptureType", + ) + ) + number_of_records_in_transaction: int = proto.Field( + proto.INT32, + number=10, + ) + number_of_partitions_in_transaction: int = proto.Field( + proto.INT32, + number=11, + ) + transaction_tag: str = proto.Field( + proto.STRING, + number=12, + ) + is_system_transaction: bool = proto.Field( + proto.BOOL, + number=13, + ) + + class HeartbeatRecord(proto.Message): + r"""A heartbeat record is returned as a progress indicator, when + there are no data changes or any other partition record types in + the change stream partition. + + Attributes: + timestamp (google.protobuf.timestamp_pb2.Timestamp): + Indicates the timestamp at which the query + has returned all the records in the change + stream partition with timestamp <= heartbeat + timestamp. The heartbeat timestamp will not be + the same as the timestamps of other record types + in the same partition. + """ + + timestamp: timestamp_pb2.Timestamp = proto.Field( + proto.MESSAGE, + number=1, + message=timestamp_pb2.Timestamp, + ) + + class PartitionStartRecord(proto.Message): + r"""A partition start record serves as a notification that the + client should schedule the partitions to be queried. + PartitionStartRecord returns information about one or more + partitions. + + Attributes: + start_timestamp (google.protobuf.timestamp_pb2.Timestamp): + Start timestamp at which the partitions should be queried to + return change stream records with timestamps >= + start_timestamp. DataChangeRecord.commit_timestamps, + PartitionStartRecord.start_timestamps, + PartitionEventRecord.commit_timestamps, and + PartitionEndRecord.end_timestamps can have the same value in + the same partition. + record_sequence (str): + Record sequence numbers are unique and monotonically + increasing (but not necessarily contiguous) for a specific + timestamp across record types in the same partition. To + guarantee ordered processing, the reader should process + records (of potentially different types) in record_sequence + order for a specific timestamp in the same partition. + partition_tokens (MutableSequence[str]): + Unique partition identifiers to be used in + queries. + """ + + start_timestamp: timestamp_pb2.Timestamp = proto.Field( + proto.MESSAGE, + number=1, + message=timestamp_pb2.Timestamp, + ) + record_sequence: str = proto.Field( + proto.STRING, + number=2, + ) + partition_tokens: MutableSequence[str] = proto.RepeatedField( + proto.STRING, + number=3, + ) + + class PartitionEndRecord(proto.Message): + r"""A partition end record serves as a notification that the + client should stop reading the partition. No further records are + expected to be retrieved on it. + + Attributes: + end_timestamp (google.protobuf.timestamp_pb2.Timestamp): + End timestamp at which the change stream partition is + terminated. All changes generated by this partition will + have timestamps <= end_timestamp. + DataChangeRecord.commit_timestamps, + PartitionStartRecord.start_timestamps, + PartitionEventRecord.commit_timestamps, and + PartitionEndRecord.end_timestamps can have the same value in + the same partition. PartitionEndRecord is the last record + returned for a partition. + record_sequence (str): + Record sequence numbers are unique and monotonically + increasing (but not necessarily contiguous) for a specific + timestamp across record types in the same partition. To + guarantee ordered processing, the reader should process + records (of potentially different types) in record_sequence + order for a specific timestamp in the same partition. + partition_token (str): + Unique partition identifier describing the terminated change + stream partition. + [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEndRecord.partition_token] + is equal to the partition token of the change stream + partition currently queried to return this + PartitionEndRecord. + """ + + end_timestamp: timestamp_pb2.Timestamp = proto.Field( + proto.MESSAGE, + number=1, + message=timestamp_pb2.Timestamp, + ) + record_sequence: str = proto.Field( + proto.STRING, + number=2, + ) + partition_token: str = proto.Field( + proto.STRING, + number=3, + ) + + class PartitionEventRecord(proto.Message): + r"""A partition event record describes key range changes for a change + stream partition. The changes to a row defined by its primary key + can be captured in one change stream partition for a specific time + range, and then be captured in a different change stream partition + for a different time range. This movement of key ranges across + change stream partitions is a reflection of activities, such as + Spanner's dynamic splitting and load balancing, etc. Processing this + event is needed if users want to guarantee processing of the changes + for any key in timestamp order. If time ordered processing of + changes for a primary key is not needed, this event can be ignored. + To guarantee time ordered processing for each primary key, if the + event describes move-ins, the reader of this partition needs to wait + until the readers of the source partitions have processed all + records with timestamps <= this + PartitionEventRecord.commit_timestamp, before advancing beyond this + PartitionEventRecord. If the event describes move-outs, the reader + can notify the readers of the destination partitions that they can + continue processing. + + Attributes: + commit_timestamp (google.protobuf.timestamp_pb2.Timestamp): + Indicates the commit timestamp at which the key range change + occurred. DataChangeRecord.commit_timestamps, + PartitionStartRecord.start_timestamps, + PartitionEventRecord.commit_timestamps, and + PartitionEndRecord.end_timestamps can have the same value in + the same partition. + record_sequence (str): + Record sequence numbers are unique and monotonically + increasing (but not necessarily contiguous) for a specific + timestamp across record types in the same partition. To + guarantee ordered processing, the reader should process + records (of potentially different types) in record_sequence + order for a specific timestamp in the same partition. + partition_token (str): + Unique partition identifier describing the partition this + event occurred on. + [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token] + is equal to the partition token of the change stream + partition currently queried to return this + PartitionEventRecord. + move_in_events (MutableSequence[google.cloud.spanner_v1.types.ChangeStreamRecord.PartitionEventRecord.MoveInEvent]): + Set when one or more key ranges are moved into the change + stream partition identified by + [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token]. + + Example: Two key ranges are moved into partition (P1) from + partition (P2) and partition (P3) in a single transaction at + timestamp T. + + The PartitionEventRecord returned in P1 will reflect the + move as: + + PartitionEventRecord { commit_timestamp: T partition_token: + "P1" move_in_events { source_partition_token: "P2" } + move_in_events { source_partition_token: "P3" } } + + The PartitionEventRecord returned in P2 will reflect the + move as: + + PartitionEventRecord { commit_timestamp: T partition_token: + "P2" move_out_events { destination_partition_token: "P1" } } + + The PartitionEventRecord returned in P3 will reflect the + move as: + + PartitionEventRecord { commit_timestamp: T partition_token: + "P3" move_out_events { destination_partition_token: "P1" } } + move_out_events (MutableSequence[google.cloud.spanner_v1.types.ChangeStreamRecord.PartitionEventRecord.MoveOutEvent]): + Set when one or more key ranges are moved out of the change + stream partition identified by + [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token]. + + Example: Two key ranges are moved out of partition (P1) to + partition (P2) and partition (P3) in a single transaction at + timestamp T. + + The PartitionEventRecord returned in P1 will reflect the + move as: + + PartitionEventRecord { commit_timestamp: T partition_token: + "P1" move_out_events { destination_partition_token: "P2" } + move_out_events { destination_partition_token: "P3" } } + + The PartitionEventRecord returned in P2 will reflect the + move as: + + PartitionEventRecord { commit_timestamp: T partition_token: + "P2" move_in_events { source_partition_token: "P1" } } + + The PartitionEventRecord returned in P3 will reflect the + move as: + + PartitionEventRecord { commit_timestamp: T partition_token: + "P3" move_in_events { source_partition_token: "P1" } } + """ + + class MoveInEvent(proto.Message): + r"""Describes move-in of the key ranges into the change stream partition + identified by + [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token]. + + To maintain processing the changes for a particular key in timestamp + order, the query processing the change stream partition identified + by + [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token] + should not advance beyond the partition event record commit + timestamp until the queries processing the source change stream + partitions have processed all change stream records with timestamps + <= the partition event record commit timestamp. + + Attributes: + source_partition_token (str): + An unique partition identifier describing the + source change stream partition that recorded + changes for the key range that is moving into + this partition. + """ + + source_partition_token: str = proto.Field( + proto.STRING, + number=1, + ) + + class MoveOutEvent(proto.Message): + r"""Describes move-out of the key ranges out of the change stream + partition identified by + [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token]. + + To maintain processing the changes for a particular key in timestamp + order, the query processing the + [MoveOutEvent][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.MoveOutEvent] + in the partition identified by + [partition_token][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.partition_token] + should inform the queries processing the destination partitions that + they can unblock and proceed processing records past the + [commit_timestamp][google.spanner.v1.ChangeStreamRecord.PartitionEventRecord.commit_timestamp]. + + Attributes: + destination_partition_token (str): + An unique partition identifier describing the + destination change stream partition that will + record changes for the key range that is moving + out of this partition. + """ + + destination_partition_token: str = proto.Field( + proto.STRING, + number=1, + ) + + commit_timestamp: timestamp_pb2.Timestamp = proto.Field( + proto.MESSAGE, + number=1, + message=timestamp_pb2.Timestamp, + ) + record_sequence: str = proto.Field( + proto.STRING, + number=2, + ) + partition_token: str = proto.Field( + proto.STRING, + number=3, + ) + move_in_events: MutableSequence[ + "ChangeStreamRecord.PartitionEventRecord.MoveInEvent" + ] = proto.RepeatedField( + proto.MESSAGE, + number=4, + message="ChangeStreamRecord.PartitionEventRecord.MoveInEvent", + ) + move_out_events: MutableSequence[ + "ChangeStreamRecord.PartitionEventRecord.MoveOutEvent" + ] = proto.RepeatedField( + proto.MESSAGE, + number=5, + message="ChangeStreamRecord.PartitionEventRecord.MoveOutEvent", + ) + + data_change_record: DataChangeRecord = proto.Field( + proto.MESSAGE, + number=1, + oneof="record", + message=DataChangeRecord, + ) + heartbeat_record: HeartbeatRecord = proto.Field( + proto.MESSAGE, + number=2, + oneof="record", + message=HeartbeatRecord, + ) + partition_start_record: PartitionStartRecord = proto.Field( + proto.MESSAGE, + number=3, + oneof="record", + message=PartitionStartRecord, + ) + partition_end_record: PartitionEndRecord = proto.Field( + proto.MESSAGE, + number=4, + oneof="record", + message=PartitionEndRecord, + ) + partition_event_record: PartitionEventRecord = proto.Field( + proto.MESSAGE, + number=5, + oneof="record", + message=PartitionEventRecord, + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/spanner_v1/types/commit_response.py b/google/cloud/spanner_v1/types/commit_response.py index 2b0c504b6a..8214973e5a 100644 --- a/google/cloud/spanner_v1/types/commit_response.py +++ b/google/cloud/spanner_v1/types/commit_response.py @@ -41,15 +41,20 @@ class CommitResponse(proto.Message): The Cloud Spanner timestamp at which the transaction committed. commit_stats (google.cloud.spanner_v1.types.CommitResponse.CommitStats): - The statistics about this Commit. Not returned by default. - For more information, see + The statistics about this ``Commit``. Not returned by + default. For more information, see [CommitRequest.return_commit_stats][google.spanner.v1.CommitRequest.return_commit_stats]. precommit_token (google.cloud.spanner_v1.types.MultiplexedSessionPrecommitToken): If specified, transaction has not committed - yet. Clients must retry the commit with the new + yet. You must retry the commit with the new precommit token. This field is a member of `oneof`_ ``MultiplexedSessionRetry``. + snapshot_timestamp (google.protobuf.timestamp_pb2.Timestamp): + If ``TransactionOptions.isolation_level`` is set to + ``IsolationLevel.REPEATABLE_READ``, then the snapshot + timestamp is the timestamp at which all reads in the + transaction ran. This timestamp is never returned. """ class CommitStats(proto.Message): @@ -89,6 +94,11 @@ class CommitStats(proto.Message): oneof="MultiplexedSessionRetry", message=transaction.MultiplexedSessionPrecommitToken, ) + snapshot_timestamp: timestamp_pb2.Timestamp = proto.Field( + proto.MESSAGE, + number=5, + message=timestamp_pb2.Timestamp, + ) __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/spanner_v1/types/transaction.py b/google/cloud/spanner_v1/types/transaction.py index d088fa6570..9291501c21 100644 --- a/google/cloud/spanner_v1/types/transaction.py +++ b/google/cloud/spanner_v1/types/transaction.py @@ -35,337 +35,7 @@ class TransactionOptions(proto.Message): - r"""Transactions: - - Each session can have at most one active transaction at a time (note - that standalone reads and queries use a transaction internally and - do count towards the one transaction limit). After the active - transaction is completed, the session can immediately be re-used for - the next transaction. It is not necessary to create a new session - for each transaction. - - Transaction modes: - - Cloud Spanner supports three transaction modes: - - 1. Locking read-write. This type of transaction is the only way to - write data into Cloud Spanner. These transactions rely on - pessimistic locking and, if necessary, two-phase commit. Locking - read-write transactions may abort, requiring the application to - retry. - - 2. Snapshot read-only. Snapshot read-only transactions provide - guaranteed consistency across several reads, but do not allow - writes. Snapshot read-only transactions can be configured to read - at timestamps in the past, or configured to perform a strong read - (where Spanner will select a timestamp such that the read is - guaranteed to see the effects of all transactions that have - committed before the start of the read). Snapshot read-only - transactions do not need to be committed. - - Queries on change streams must be performed with the snapshot - read-only transaction mode, specifying a strong read. Please see - [TransactionOptions.ReadOnly.strong][google.spanner.v1.TransactionOptions.ReadOnly.strong] - for more details. - - 3. Partitioned DML. This type of transaction is used to execute a - single Partitioned DML statement. Partitioned DML partitions the - key space and runs the DML statement over each partition in - parallel using separate, internal transactions that commit - independently. Partitioned DML transactions do not need to be - committed. - - For transactions that only read, snapshot read-only transactions - provide simpler semantics and are almost always faster. In - particular, read-only transactions do not take locks, so they do not - conflict with read-write transactions. As a consequence of not - taking locks, they also do not abort, so retry loops are not needed. - - Transactions may only read-write data in a single database. They - may, however, read-write data in different tables within that - database. - - Locking read-write transactions: - - Locking transactions may be used to atomically read-modify-write - data anywhere in a database. This type of transaction is externally - consistent. - - Clients should attempt to minimize the amount of time a transaction - is active. Faster transactions commit with higher probability and - cause less contention. Cloud Spanner attempts to keep read locks - active as long as the transaction continues to do reads, and the - transaction has not been terminated by - [Commit][google.spanner.v1.Spanner.Commit] or - [Rollback][google.spanner.v1.Spanner.Rollback]. Long periods of - inactivity at the client may cause Cloud Spanner to release a - transaction's locks and abort it. - - Conceptually, a read-write transaction consists of zero or more - reads or SQL statements followed by - [Commit][google.spanner.v1.Spanner.Commit]. At any time before - [Commit][google.spanner.v1.Spanner.Commit], the client can send a - [Rollback][google.spanner.v1.Spanner.Rollback] request to abort the - transaction. - - Semantics: - - Cloud Spanner can commit the transaction if all read locks it - acquired are still valid at commit time, and it is able to acquire - write locks for all writes. Cloud Spanner can abort the transaction - for any reason. If a commit attempt returns ``ABORTED``, Cloud - Spanner guarantees that the transaction has not modified any user - data in Cloud Spanner. - - Unless the transaction commits, Cloud Spanner makes no guarantees - about how long the transaction's locks were held for. It is an error - to use Cloud Spanner locks for any sort of mutual exclusion other - than between Cloud Spanner transactions themselves. - - Retrying aborted transactions: - - When a transaction aborts, the application can choose to retry the - whole transaction again. To maximize the chances of successfully - committing the retry, the client should execute the retry in the - same session as the original attempt. The original session's lock - priority increases with each consecutive abort, meaning that each - attempt has a slightly better chance of success than the previous. - - Under some circumstances (for example, many transactions attempting - to modify the same row(s)), a transaction can abort many times in a - short period before successfully committing. Thus, it is not a good - idea to cap the number of retries a transaction can attempt; - instead, it is better to limit the total amount of time spent - retrying. - - Idle transactions: - - A transaction is considered idle if it has no outstanding reads or - SQL queries and has not started a read or SQL query within the last - 10 seconds. Idle transactions can be aborted by Cloud Spanner so - that they don't hold on to locks indefinitely. If an idle - transaction is aborted, the commit will fail with error ``ABORTED``. - - If this behavior is undesirable, periodically executing a simple SQL - query in the transaction (for example, ``SELECT 1``) prevents the - transaction from becoming idle. - - Snapshot read-only transactions: - - Snapshot read-only transactions provides a simpler method than - locking read-write transactions for doing several consistent reads. - However, this type of transaction does not support writes. - - Snapshot transactions do not take locks. Instead, they work by - choosing a Cloud Spanner timestamp, then executing all reads at that - timestamp. Since they do not acquire locks, they do not block - concurrent read-write transactions. - - Unlike locking read-write transactions, snapshot read-only - transactions never abort. They can fail if the chosen read timestamp - is garbage collected; however, the default garbage collection policy - is generous enough that most applications do not need to worry about - this in practice. - - Snapshot read-only transactions do not need to call - [Commit][google.spanner.v1.Spanner.Commit] or - [Rollback][google.spanner.v1.Spanner.Rollback] (and in fact are not - permitted to do so). - - To execute a snapshot transaction, the client specifies a timestamp - bound, which tells Cloud Spanner how to choose a read timestamp. - - The types of timestamp bound are: - - - Strong (the default). - - Bounded staleness. - - Exact staleness. - - If the Cloud Spanner database to be read is geographically - distributed, stale read-only transactions can execute more quickly - than strong or read-write transactions, because they are able to - execute far from the leader replica. - - Each type of timestamp bound is discussed in detail below. - - Strong: Strong reads are guaranteed to see the effects of all - transactions that have committed before the start of the read. - Furthermore, all rows yielded by a single read are consistent with - each other -- if any part of the read observes a transaction, all - parts of the read see the transaction. - - Strong reads are not repeatable: two consecutive strong read-only - transactions might return inconsistent results if there are - concurrent writes. If consistency across reads is required, the - reads should be executed within a transaction or at an exact read - timestamp. - - Queries on change streams (see below for more details) must also - specify the strong read timestamp bound. - - See - [TransactionOptions.ReadOnly.strong][google.spanner.v1.TransactionOptions.ReadOnly.strong]. - - Exact staleness: - - These timestamp bounds execute reads at a user-specified timestamp. - Reads at a timestamp are guaranteed to see a consistent prefix of - the global transaction history: they observe modifications done by - all transactions with a commit timestamp less than or equal to the - read timestamp, and observe none of the modifications done by - transactions with a larger commit timestamp. They will block until - all conflicting transactions that may be assigned commit timestamps - <= the read timestamp have finished. - - The timestamp can either be expressed as an absolute Cloud Spanner - commit timestamp or a staleness relative to the current time. - - These modes do not require a "negotiation phase" to pick a - timestamp. As a result, they execute slightly faster than the - equivalent boundedly stale concurrency modes. On the other hand, - boundedly stale reads usually return fresher results. - - See - [TransactionOptions.ReadOnly.read_timestamp][google.spanner.v1.TransactionOptions.ReadOnly.read_timestamp] - and - [TransactionOptions.ReadOnly.exact_staleness][google.spanner.v1.TransactionOptions.ReadOnly.exact_staleness]. - - Bounded staleness: - - Bounded staleness modes allow Cloud Spanner to pick the read - timestamp, subject to a user-provided staleness bound. Cloud Spanner - chooses the newest timestamp within the staleness bound that allows - execution of the reads at the closest available replica without - blocking. - - All rows yielded are consistent with each other -- if any part of - the read observes a transaction, all parts of the read see the - transaction. Boundedly stale reads are not repeatable: two stale - reads, even if they use the same staleness bound, can execute at - different timestamps and thus return inconsistent results. - - Boundedly stale reads execute in two phases: the first phase - negotiates a timestamp among all replicas needed to serve the read. - In the second phase, reads are executed at the negotiated timestamp. - - As a result of the two phase execution, bounded staleness reads are - usually a little slower than comparable exact staleness reads. - However, they are typically able to return fresher results, and are - more likely to execute at the closest replica. - - Because the timestamp negotiation requires up-front knowledge of - which rows will be read, it can only be used with single-use - read-only transactions. - - See - [TransactionOptions.ReadOnly.max_staleness][google.spanner.v1.TransactionOptions.ReadOnly.max_staleness] - and - [TransactionOptions.ReadOnly.min_read_timestamp][google.spanner.v1.TransactionOptions.ReadOnly.min_read_timestamp]. - - Old read timestamps and garbage collection: - - Cloud Spanner continuously garbage collects deleted and overwritten - data in the background to reclaim storage space. This process is - known as "version GC". By default, version GC reclaims versions - after they are one hour old. Because of this, Cloud Spanner cannot - perform reads at read timestamps more than one hour in the past. - This restriction also applies to in-progress reads and/or SQL - queries whose timestamp become too old while executing. Reads and - SQL queries with too-old read timestamps fail with the error - ``FAILED_PRECONDITION``. - - You can configure and extend the ``VERSION_RETENTION_PERIOD`` of a - database up to a period as long as one week, which allows Cloud - Spanner to perform reads up to one week in the past. - - Querying change Streams: - - A Change Stream is a schema object that can be configured to watch - data changes on the entire database, a set of tables, or a set of - columns in a database. - - When a change stream is created, Spanner automatically defines a - corresponding SQL Table-Valued Function (TVF) that can be used to - query the change records in the associated change stream using the - ExecuteStreamingSql API. The name of the TVF for a change stream is - generated from the name of the change stream: - READ_. - - All queries on change stream TVFs must be executed using the - ExecuteStreamingSql API with a single-use read-only transaction with - a strong read-only timestamp_bound. The change stream TVF allows - users to specify the start_timestamp and end_timestamp for the time - range of interest. All change records within the retention period is - accessible using the strong read-only timestamp_bound. All other - TransactionOptions are invalid for change stream queries. - - In addition, if TransactionOptions.read_only.return_read_timestamp - is set to true, a special value of 2^63 - 2 will be returned in the - [Transaction][google.spanner.v1.Transaction] message that describes - the transaction, instead of a valid read timestamp. This special - value should be discarded and not used for any subsequent queries. - - Please see https://cloud.google.com/spanner/docs/change-streams for - more details on how to query the change stream TVFs. - - Partitioned DML transactions: - - Partitioned DML transactions are used to execute DML statements with - a different execution strategy that provides different, and often - better, scalability properties for large, table-wide operations than - DML in a ReadWrite transaction. Smaller scoped statements, such as - an OLTP workload, should prefer using ReadWrite transactions. - - Partitioned DML partitions the keyspace and runs the DML statement - on each partition in separate, internal transactions. These - transactions commit automatically when complete, and run - independently from one another. - - To reduce lock contention, this execution strategy only acquires - read locks on rows that match the WHERE clause of the statement. - Additionally, the smaller per-partition transactions hold locks for - less time. - - That said, Partitioned DML is not a drop-in replacement for standard - DML used in ReadWrite transactions. - - - The DML statement must be fully-partitionable. Specifically, the - statement must be expressible as the union of many statements - which each access only a single row of the table. - - - The statement is not applied atomically to all rows of the table. - Rather, the statement is applied atomically to partitions of the - table, in independent transactions. Secondary index rows are - updated atomically with the base table rows. - - - Partitioned DML does not guarantee exactly-once execution - semantics against a partition. The statement will be applied at - least once to each partition. It is strongly recommended that the - DML statement should be idempotent to avoid unexpected results. - For instance, it is potentially dangerous to run a statement such - as ``UPDATE table SET column = column + 1`` as it could be run - multiple times against some rows. - - - The partitions are committed automatically - there is no support - for Commit or Rollback. If the call returns an error, or if the - client issuing the ExecuteSql call dies, it is possible that some - rows had the statement executed on them successfully. It is also - possible that statement was never executed against other rows. - - - Partitioned DML transactions may only contain the execution of a - single DML statement via ExecuteSql or ExecuteStreamingSql. - - - If any error is encountered during the execution of the - partitioned DML operation (for instance, a UNIQUE INDEX - violation, division by zero, or a value that cannot be stored due - to schema constraints), then the operation is stopped at that - point and an error is returned. It is possible that at this - point, some partitions have been committed (or even committed - multiple times), and other partitions have not been run at all. - - Given the above, Partitioned DML is good fit for large, - database-wide, operations that are idempotent, such as deleting old - rows from a very large table. + r"""Options to use for transactions. This message has `oneof`_ fields (mutually exclusive fields). For each oneof, at most one member field can be set at the same time. @@ -393,7 +63,7 @@ class TransactionOptions(proto.Message): This field is a member of `oneof`_ ``mode``. read_only (google.cloud.spanner_v1.types.TransactionOptions.ReadOnly): - Transaction will not write. + Transaction does not write. Authorization to begin a read-only transaction requires ``spanner.databases.beginReadOnlyTransaction`` permission on @@ -401,24 +71,26 @@ class TransactionOptions(proto.Message): This field is a member of `oneof`_ ``mode``. exclude_txn_from_change_streams (bool): - When ``exclude_txn_from_change_streams`` is set to ``true``: + When ``exclude_txn_from_change_streams`` is set to ``true``, + it prevents read or write transactions from being tracked in + change streams. - - Mutations from this transaction will not be recorded in - change streams with DDL option - ``allow_txn_exclusion=true`` that are tracking columns - modified by these transactions. - - Mutations from this transaction will be recorded in - change streams with DDL option - ``allow_txn_exclusion=false or not set`` that are - tracking columns modified by these transactions. + - If the DDL option ``allow_txn_exclusion`` is set to + ``true``, then the updates made within this transaction + aren't recorded in the change stream. + + - If you don't set the DDL option ``allow_txn_exclusion`` + or if it's set to ``false``, then the updates made within + this transaction are recorded in the change stream. When ``exclude_txn_from_change_streams`` is set to ``false`` - or not set, mutations from this transaction will be recorded + or not set, modifications from this transaction are recorded in all change streams that are tracking columns modified by - these transactions. ``exclude_txn_from_change_streams`` may - only be specified for read-write or partitioned-dml - transactions, otherwise the API will return an - ``INVALID_ARGUMENT`` error. + these transactions. + + The ``exclude_txn_from_change_streams`` option can only be + specified for read-write or partitioned DML transactions, + otherwise the API returns an ``INVALID_ARGUMENT`` error. isolation_level (google.cloud.spanner_v1.types.TransactionOptions.IsolationLevel): Isolation level for the transaction. """ @@ -447,8 +119,8 @@ class IsolationLevel(proto.Enum): https://cloud.google.com/spanner/docs/true-time-external-consistency#serializability. REPEATABLE_READ (2): All reads performed during the transaction observe a - consistent snapshot of the database, and the transaction - will only successfully commit in the absence of conflicts + consistent snapshot of the database, and the transaction is + only successfully committed in the absence of conflicts between its updates and any concurrent updates that have occurred since that snapshot. Consequently, in contrast to ``SERIALIZABLE`` transactions, only write-write conflicts @@ -477,8 +149,6 @@ class ReadWrite(proto.Message): ID of the previous transaction attempt that was aborted if this transaction is being executed on a multiplexed session. - This feature is not yet supported and will - result in an UNIMPLEMENTED error. """ class ReadLockMode(proto.Enum): @@ -489,26 +159,29 @@ class ReadLockMode(proto.Enum): READ_LOCK_MODE_UNSPECIFIED (0): Default value. - - If isolation level is ``REPEATABLE_READ``, then it is an - error to specify ``read_lock_mode``. Locking semantics - default to ``OPTIMISTIC``. No validation checks are done - for reads, except for: + - If isolation level is + [REPEATABLE_READ][google.spanner.v1.TransactionOptions.IsolationLevel.REPEATABLE_READ], + then it is an error to specify ``read_lock_mode``. + Locking semantics default to ``OPTIMISTIC``. No + validation checks are done for reads, except to validate + that the data that was served at the snapshot time is + unchanged at commit time in the following cases: 1. reads done as part of queries that use ``SELECT FOR UPDATE`` 2. reads done as part of statements with a ``LOCK_SCANNED_RANGES`` hint - 3. reads done as part of DML statements to validate that - the data that was served at the snapshot time is - unchanged at commit time. + 3. reads done as part of DML statements - At all other isolation levels, if ``read_lock_mode`` is - the default value, then pessimistic read lock is used. + the default value, then pessimistic read locks are used. PESSIMISTIC (1): Pessimistic lock mode. Read locks are acquired immediately on read. Semantics - described only applies to ``SERIALIZABLE`` isolation. + described only applies to + [SERIALIZABLE][google.spanner.v1.TransactionOptions.IsolationLevel.SERIALIZABLE] + isolation. OPTIMISTIC (2): Optimistic lock mode. @@ -516,7 +189,8 @@ class ReadLockMode(proto.Enum): read. Instead the locks are acquired on a commit to validate that read/queried data has not changed since the transaction started. Semantics described only applies to - ``SERIALIZABLE`` isolation. + [SERIALIZABLE][google.spanner.v1.TransactionOptions.IsolationLevel.SERIALIZABLE] + isolation. """ READ_LOCK_MODE_UNSPECIFIED = 0 PESSIMISTIC = 1 @@ -586,7 +260,7 @@ class ReadOnly(proto.Message): Executes all reads at the given timestamp. Unlike other modes, reads at a specific timestamp are repeatable; the same read at the same timestamp always returns the same - data. If the timestamp is in the future, the read will block + data. If the timestamp is in the future, the read is blocked until the specified timestamp, modulo the read's deadline. Useful for large scale consistent reads such as mapreduces, @@ -703,7 +377,7 @@ class Transaction(proto.Message): A timestamp in RFC3339 UTC "Zulu" format, accurate to nanoseconds. Example: ``"2014-10-02T15:01:23.045123456Z"``. precommit_token (google.cloud.spanner_v1.types.MultiplexedSessionPrecommitToken): - A precommit token will be included in the response of a + A precommit token is included in the response of a BeginTransaction request if the read-write transaction is on a multiplexed session and a mutation_key was specified in the @@ -711,8 +385,7 @@ class Transaction(proto.Message): The precommit token with the highest sequence number from this transaction attempt should be passed to the [Commit][google.spanner.v1.Spanner.Commit] request for this - transaction. This feature is not yet supported and will - result in an UNIMPLEMENTED error. + transaction. """ id: bytes = proto.Field( @@ -791,8 +464,11 @@ class TransactionSelector(proto.Message): class MultiplexedSessionPrecommitToken(proto.Message): r"""When a read-write transaction is executed on a multiplexed session, this precommit token is sent back to the client as a part of the - [Transaction] message in the BeginTransaction response and also as a - part of the [ResultSet] and [PartialResultSet] responses. + [Transaction][google.spanner.v1.Transaction] message in the + [BeginTransaction][google.spanner.v1.BeginTransactionRequest] + response and also as a part of the + [ResultSet][google.spanner.v1.ResultSet] and + [PartialResultSet][google.spanner.v1.PartialResultSet] responses. Attributes: precommit_token (bytes): diff --git a/samples/generated_samples/snippet_metadata_google.spanner.admin.database.v1.json b/samples/generated_samples/snippet_metadata_google.spanner.admin.database.v1.json index 609e70a8c2..f6bcc86bf4 100644 --- a/samples/generated_samples/snippet_metadata_google.spanner.admin.database.v1.json +++ b/samples/generated_samples/snippet_metadata_google.spanner.admin.database.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-spanner-admin-database", - "version": "3.55.0" + "version": "0.1.0" }, "snippets": [ { @@ -2158,6 +2158,175 @@ ], "title": "spanner_v1_generated_database_admin_get_iam_policy_sync.py" }, + { + "canonical": true, + "clientMethod": { + "async": true, + "client": { + "fullName": "google.cloud.spanner_admin_database_v1.DatabaseAdminAsyncClient", + "shortName": "DatabaseAdminAsyncClient" + }, + "fullName": "google.cloud.spanner_admin_database_v1.DatabaseAdminAsyncClient.internal_update_graph_operation", + "method": { + "fullName": "google.spanner.admin.database.v1.DatabaseAdmin.InternalUpdateGraphOperation", + "service": { + "fullName": "google.spanner.admin.database.v1.DatabaseAdmin", + "shortName": "DatabaseAdmin" + }, + "shortName": "InternalUpdateGraphOperation" + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.spanner_admin_database_v1.types.InternalUpdateGraphOperationRequest" + }, + { + "name": "database", + "type": "str" + }, + { + "name": "operation_id", + "type": "str" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, Union[str, bytes]]]" + } + ], + "resultType": "google.cloud.spanner_admin_database_v1.types.InternalUpdateGraphOperationResponse", + "shortName": "internal_update_graph_operation" + }, + "description": "Sample for InternalUpdateGraphOperation", + "file": "spanner_v1_generated_database_admin_internal_update_graph_operation_async.py", + "language": "PYTHON", + "origin": "API_DEFINITION", + "regionTag": "spanner_v1_generated_DatabaseAdmin_InternalUpdateGraphOperation_async", + "segments": [ + { + "end": 53, + "start": 27, + "type": "FULL" + }, + { + "end": 53, + "start": 27, + "type": "SHORT" + }, + { + "end": 40, + "start": 38, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 47, + "start": 41, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 50, + "start": 48, + "type": "REQUEST_EXECUTION" + }, + { + "end": 54, + "start": 51, + "type": "RESPONSE_HANDLING" + } + ], + "title": "spanner_v1_generated_database_admin_internal_update_graph_operation_async.py" + }, + { + "canonical": true, + "clientMethod": { + "client": { + "fullName": "google.cloud.spanner_admin_database_v1.DatabaseAdminClient", + "shortName": "DatabaseAdminClient" + }, + "fullName": "google.cloud.spanner_admin_database_v1.DatabaseAdminClient.internal_update_graph_operation", + "method": { + "fullName": "google.spanner.admin.database.v1.DatabaseAdmin.InternalUpdateGraphOperation", + "service": { + "fullName": "google.spanner.admin.database.v1.DatabaseAdmin", + "shortName": "DatabaseAdmin" + }, + "shortName": "InternalUpdateGraphOperation" + }, + "parameters": [ + { + "name": "request", + "type": "google.cloud.spanner_admin_database_v1.types.InternalUpdateGraphOperationRequest" + }, + { + "name": "database", + "type": "str" + }, + { + "name": "operation_id", + "type": "str" + }, + { + "name": "retry", + "type": "google.api_core.retry.Retry" + }, + { + "name": "timeout", + "type": "float" + }, + { + "name": "metadata", + "type": "Sequence[Tuple[str, Union[str, bytes]]]" + } + ], + "resultType": "google.cloud.spanner_admin_database_v1.types.InternalUpdateGraphOperationResponse", + "shortName": "internal_update_graph_operation" + }, + "description": "Sample for InternalUpdateGraphOperation", + "file": "spanner_v1_generated_database_admin_internal_update_graph_operation_sync.py", + "language": "PYTHON", + "origin": "API_DEFINITION", + "regionTag": "spanner_v1_generated_DatabaseAdmin_InternalUpdateGraphOperation_sync", + "segments": [ + { + "end": 53, + "start": 27, + "type": "FULL" + }, + { + "end": 53, + "start": 27, + "type": "SHORT" + }, + { + "end": 40, + "start": 38, + "type": "CLIENT_INITIALIZATION" + }, + { + "end": 47, + "start": 41, + "type": "REQUEST_INITIALIZATION" + }, + { + "end": 50, + "start": 48, + "type": "REQUEST_EXECUTION" + }, + { + "end": 54, + "start": 51, + "type": "RESPONSE_HANDLING" + } + ], + "title": "spanner_v1_generated_database_admin_internal_update_graph_operation_sync.py" + }, { "canonical": true, "clientMethod": { diff --git a/samples/generated_samples/snippet_metadata_google.spanner.admin.instance.v1.json b/samples/generated_samples/snippet_metadata_google.spanner.admin.instance.v1.json index c78d74fd41..06d6291f45 100644 --- a/samples/generated_samples/snippet_metadata_google.spanner.admin.instance.v1.json +++ b/samples/generated_samples/snippet_metadata_google.spanner.admin.instance.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-spanner-admin-instance", - "version": "3.55.0" + "version": "0.1.0" }, "snippets": [ { diff --git a/samples/generated_samples/snippet_metadata_google.spanner.v1.json b/samples/generated_samples/snippet_metadata_google.spanner.v1.json index 22a0a46fb4..727606e51f 100644 --- a/samples/generated_samples/snippet_metadata_google.spanner.v1.json +++ b/samples/generated_samples/snippet_metadata_google.spanner.v1.json @@ -8,7 +8,7 @@ ], "language": "PYTHON", "name": "google-cloud-spanner", - "version": "3.55.0" + "version": "0.1.0" }, "snippets": [ { diff --git a/samples/generated_samples/spanner_v1_generated_database_admin_internal_update_graph_operation_async.py b/samples/generated_samples/spanner_v1_generated_database_admin_internal_update_graph_operation_async.py new file mode 100644 index 0000000000..556205a0aa --- /dev/null +++ b/samples/generated_samples/spanner_v1_generated_database_admin_internal_update_graph_operation_async.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 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 InternalUpdateGraphOperation +# 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-spanner-admin-database + + +# [START spanner_v1_generated_DatabaseAdmin_InternalUpdateGraphOperation_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 spanner_admin_database_v1 + + +async def sample_internal_update_graph_operation(): + # Create a client + client = spanner_admin_database_v1.DatabaseAdminAsyncClient() + + # Initialize request argument(s) + request = spanner_admin_database_v1.InternalUpdateGraphOperationRequest( + database="database_value", + operation_id="operation_id_value", + vm_identity_token="vm_identity_token_value", + ) + + # Make the request + response = await client.internal_update_graph_operation(request=request) + + # Handle the response + print(response) + +# [END spanner_v1_generated_DatabaseAdmin_InternalUpdateGraphOperation_async] diff --git a/samples/generated_samples/spanner_v1_generated_database_admin_internal_update_graph_operation_sync.py b/samples/generated_samples/spanner_v1_generated_database_admin_internal_update_graph_operation_sync.py new file mode 100644 index 0000000000..46f1a3c88f --- /dev/null +++ b/samples/generated_samples/spanner_v1_generated_database_admin_internal_update_graph_operation_sync.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Copyright 2025 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 InternalUpdateGraphOperation +# 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-spanner-admin-database + + +# [START spanner_v1_generated_DatabaseAdmin_InternalUpdateGraphOperation_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 spanner_admin_database_v1 + + +def sample_internal_update_graph_operation(): + # Create a client + client = spanner_admin_database_v1.DatabaseAdminClient() + + # Initialize request argument(s) + request = spanner_admin_database_v1.InternalUpdateGraphOperationRequest( + database="database_value", + operation_id="operation_id_value", + vm_identity_token="vm_identity_token_value", + ) + + # Make the request + response = client.internal_update_graph_operation(request=request) + + # Handle the response + print(response) + +# [END spanner_v1_generated_DatabaseAdmin_InternalUpdateGraphOperation_sync] diff --git a/scripts/fixup_spanner_admin_database_v1_keywords.py b/scripts/fixup_spanner_admin_database_v1_keywords.py index c4ab94b57c..d642e9a0e3 100644 --- a/scripts/fixup_spanner_admin_database_v1_keywords.py +++ b/scripts/fixup_spanner_admin_database_v1_keywords.py @@ -52,6 +52,7 @@ class spanner_admin_databaseCallTransformer(cst.CSTTransformer): 'get_database': ('name', ), 'get_database_ddl': ('database', ), 'get_iam_policy': ('resource', 'options', ), + 'internal_update_graph_operation': ('database', 'operation_id', 'vm_identity_token', 'progress', 'status', ), 'list_backup_operations': ('parent', 'filter', 'page_size', 'page_token', ), 'list_backups': ('parent', 'filter', 'page_size', 'page_token', ), 'list_backup_schedules': ('parent', 'page_size', 'page_token', ), diff --git a/tests/unit/gapic/spanner_admin_database_v1/test_database_admin.py b/tests/unit/gapic/spanner_admin_database_v1/test_database_admin.py index beda28dad6..f62b95c85d 100644 --- a/tests/unit/gapic/spanner_admin_database_v1/test_database_admin.py +++ b/tests/unit/gapic/spanner_admin_database_v1/test_database_admin.py @@ -14,6 +14,7 @@ # limitations under the License. # import os +import re # try/except added for compatibility with python < 3.8 try: @@ -11260,6 +11261,302 @@ async def test_list_backup_schedules_async_pages(): assert page_.raw_page.next_page_token == token +@pytest.mark.parametrize( + "request_type", + [ + spanner_database_admin.InternalUpdateGraphOperationRequest, + dict, + ], +) +def test_internal_update_graph_operation(request_type, transport: str = "grpc"): + client = DatabaseAdminClient( + 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.internal_update_graph_operation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = ( + spanner_database_admin.InternalUpdateGraphOperationResponse() + ) + response = client.internal_update_graph_operation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + request = spanner_database_admin.InternalUpdateGraphOperationRequest() + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance( + response, spanner_database_admin.InternalUpdateGraphOperationResponse + ) + + +def test_internal_update_graph_operation_non_empty_request_with_auto_populated_field(): + # This test is a coverage failsafe to make sure that UUID4 fields are + # automatically populated, according to AIP-4235, with non-empty requests. + client = DatabaseAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Populate all string fields in the request which are not UUID4 + # since we want to check that UUID4 are populated automatically + # if they meet the requirements of AIP 4235. + request = spanner_database_admin.InternalUpdateGraphOperationRequest( + database="database_value", + operation_id="operation_id_value", + vm_identity_token="vm_identity_token_value", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.internal_update_graph_operation), "__call__" + ) as call: + call.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client.internal_update_graph_operation(request=request) + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == spanner_database_admin.InternalUpdateGraphOperationRequest( + database="database_value", + operation_id="operation_id_value", + vm_identity_token="vm_identity_token_value", + ) + + +def test_internal_update_graph_operation_use_cached_wrapped_rpc(): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method.wrap_method") as wrapper_fn: + client = DatabaseAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._transport.internal_update_graph_operation + in client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.Mock() + mock_rpc.return_value.name = ( + "foo" # operation_request.operation in compute client(s) expect a string. + ) + client._transport._wrapped_methods[ + client._transport.internal_update_graph_operation + ] = mock_rpc + request = {} + client.internal_update_graph_operation(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + client.internal_update_graph_operation(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + +@pytest.mark.asyncio +async def test_internal_update_graph_operation_async_use_cached_wrapped_rpc( + transport: str = "grpc_asyncio", +): + # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, + # instead of constructing them on each call + with mock.patch("google.api_core.gapic_v1.method_async.wrap_method") as wrapper_fn: + client = DatabaseAdminAsyncClient( + credentials=async_anonymous_credentials(), + transport=transport, + ) + + # Should wrap all calls on client creation + assert wrapper_fn.call_count > 0 + wrapper_fn.reset_mock() + + # Ensure method has been cached + assert ( + client._client._transport.internal_update_graph_operation + in client._client._transport._wrapped_methods + ) + + # Replace cached wrapped function with mock + mock_rpc = mock.AsyncMock() + mock_rpc.return_value = mock.Mock() + client._client._transport._wrapped_methods[ + client._client._transport.internal_update_graph_operation + ] = mock_rpc + + request = {} + await client.internal_update_graph_operation(request) + + # Establish that the underlying gRPC stub method was called. + assert mock_rpc.call_count == 1 + + await client.internal_update_graph_operation(request) + + # Establish that a new wrapper was not created for this call + assert wrapper_fn.call_count == 0 + assert mock_rpc.call_count == 2 + + +@pytest.mark.asyncio +async def test_internal_update_graph_operation_async( + transport: str = "grpc_asyncio", + request_type=spanner_database_admin.InternalUpdateGraphOperationRequest, +): + client = DatabaseAdminAsyncClient( + credentials=async_anonymous_credentials(), + 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.internal_update_graph_operation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + spanner_database_admin.InternalUpdateGraphOperationResponse() + ) + response = await client.internal_update_graph_operation(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + request = spanner_database_admin.InternalUpdateGraphOperationRequest() + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance( + response, spanner_database_admin.InternalUpdateGraphOperationResponse + ) + + +@pytest.mark.asyncio +async def test_internal_update_graph_operation_async_from_dict(): + await test_internal_update_graph_operation_async(request_type=dict) + + +def test_internal_update_graph_operation_flattened(): + client = DatabaseAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.internal_update_graph_operation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = ( + spanner_database_admin.InternalUpdateGraphOperationResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.internal_update_graph_operation( + database="database_value", + operation_id="operation_id_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + arg = args[0].database + mock_val = "database_value" + assert arg == mock_val + arg = args[0].operation_id + mock_val = "operation_id_value" + assert arg == mock_val + + +def test_internal_update_graph_operation_flattened_error(): + client = DatabaseAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + client.internal_update_graph_operation( + spanner_database_admin.InternalUpdateGraphOperationRequest(), + database="database_value", + operation_id="operation_id_value", + ) + + +@pytest.mark.asyncio +async def test_internal_update_graph_operation_flattened_async(): + client = DatabaseAdminAsyncClient( + credentials=async_anonymous_credentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.internal_update_graph_operation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = ( + spanner_database_admin.InternalUpdateGraphOperationResponse() + ) + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + spanner_database_admin.InternalUpdateGraphOperationResponse() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.internal_update_graph_operation( + database="database_value", + operation_id="operation_id_value", + ) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + arg = args[0].database + mock_val = "database_value" + assert arg == mock_val + arg = args[0].operation_id + mock_val = "operation_id_value" + assert arg == mock_val + + +@pytest.mark.asyncio +async def test_internal_update_graph_operation_flattened_error_async(): + client = DatabaseAdminAsyncClient( + credentials=async_anonymous_credentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.internal_update_graph_operation( + spanner_database_admin.InternalUpdateGraphOperationRequest(), + database="database_value", + operation_id="operation_id_value", + ) + + def test_list_databases_rest_use_cached_wrapped_rpc(): # Clients should use _prep_wrapped_messages to create cached wrapped rpcs, # instead of constructing them on each call @@ -16613,6 +16910,30 @@ def test_list_backup_schedules_rest_pager(transport: str = "rest"): assert page_.raw_page.next_page_token == token +def test_internal_update_graph_operation_rest_no_http_options(): + client = DatabaseAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = spanner_database_admin.InternalUpdateGraphOperationRequest() + with pytest.raises(RuntimeError): + client.internal_update_graph_operation(request) + + +def test_internal_update_graph_operation_rest_error(): + client = DatabaseAdminClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + # Since a `google.api.http` annotation is required for using a rest transport + # method, this should error. + with pytest.raises(NotImplementedError) as not_implemented_error: + client.internal_update_graph_operation({}) + assert ( + "Method InternalUpdateGraphOperation is not available over REST transport" + in str(not_implemented_error.value) + ) + + def test_credentials_transport_error(): # It is an error to provide credentials and a transport instance. transport = transports.DatabaseAdminGrpcTransport( @@ -17285,6 +17606,31 @@ def test_list_backup_schedules_empty_call_grpc(): assert args[0] == request_msg +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_internal_update_graph_operation_empty_call_grpc(): + client = DatabaseAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="grpc", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.internal_update_graph_operation), "__call__" + ) as call: + call.return_value = ( + spanner_database_admin.InternalUpdateGraphOperationResponse() + ) + client.internal_update_graph_operation(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = spanner_database_admin.InternalUpdateGraphOperationRequest() + + assert args[0] == request_msg + + def test_transport_kind_grpc_asyncio(): transport = DatabaseAdminAsyncClient.get_transport_class("grpc_asyncio")( credentials=async_anonymous_credentials() @@ -18024,6 +18370,33 @@ async def test_list_backup_schedules_empty_call_grpc_asyncio(): assert args[0] == request_msg +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +@pytest.mark.asyncio +async def test_internal_update_graph_operation_empty_call_grpc_asyncio(): + client = DatabaseAdminAsyncClient( + credentials=async_anonymous_credentials(), + transport="grpc_asyncio", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.internal_update_graph_operation), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + spanner_database_admin.InternalUpdateGraphOperationResponse() + ) + await client.internal_update_graph_operation(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = spanner_database_admin.InternalUpdateGraphOperationRequest() + + assert args[0] == request_msg + + def test_transport_kind_rest(): transport = DatabaseAdminClient.get_transport_class("rest")( credentials=ga_credentials.AnonymousCredentials() @@ -21861,6 +22234,19 @@ def test_list_backup_schedules_rest_interceptors(null_interceptor): post_with_metadata.assert_called_once() +def test_internal_update_graph_operation_rest_error(): + client = DatabaseAdminClient( + credentials=ga_credentials.AnonymousCredentials(), transport="rest" + ) + + with pytest.raises(NotImplementedError) as not_implemented_error: + client.internal_update_graph_operation({}) + assert ( + "Method InternalUpdateGraphOperation is not available over REST transport" + in str(not_implemented_error.value) + ) + + def test_cancel_operation_rest_bad_request( request_type=operations_pb2.CancelOperationRequest, ): @@ -22674,6 +23060,28 @@ def test_list_backup_schedules_empty_call_rest(): assert args[0] == request_msg +# This test is a coverage failsafe to make sure that totally empty calls, +# i.e. request == None and no flattened fields passed, work. +def test_internal_update_graph_operation_empty_call_rest(): + client = DatabaseAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + # Mock the actual call, and fake the request. + with mock.patch.object( + type(client.transport.internal_update_graph_operation), "__call__" + ) as call: + client.internal_update_graph_operation(request=None) + + # Establish that the underlying stub method was called. + call.assert_called() + _, args, _ = call.mock_calls[0] + request_msg = spanner_database_admin.InternalUpdateGraphOperationRequest() + + assert args[0] == request_msg + + def test_database_admin_rest_lro_client(): client = DatabaseAdminClient( credentials=ga_credentials.AnonymousCredentials(), @@ -22750,6 +23158,7 @@ def test_database_admin_base_transport(): "update_backup_schedule", "delete_backup_schedule", "list_backup_schedules", + "internal_update_graph_operation", "get_operation", "cancel_operation", "delete_operation", @@ -23107,6 +23516,9 @@ def test_database_admin_client_transport_session_collision(transport_name): session1 = client1.transport.list_backup_schedules._session session2 = client2.transport.list_backup_schedules._session assert session1 != session2 + session1 = client1.transport.internal_update_graph_operation._session + session2 = client2.transport.internal_update_graph_operation._session + assert session1 != session2 def test_database_admin_grpc_transport_channel(): diff --git a/tests/unit/gapic/spanner_admin_instance_v1/test_instance_admin.py b/tests/unit/gapic/spanner_admin_instance_v1/test_instance_admin.py index 9d7b0bb190..52424e65d3 100644 --- a/tests/unit/gapic/spanner_admin_instance_v1/test_instance_admin.py +++ b/tests/unit/gapic/spanner_admin_instance_v1/test_instance_admin.py @@ -14,6 +14,7 @@ # limitations under the License. # import os +import re # try/except added for compatibility with python < 3.8 try: @@ -17674,6 +17675,272 @@ def test_move_instance_rest_interceptors(null_interceptor): post_with_metadata.assert_called_once() +def test_cancel_operation_rest_bad_request( + request_type=operations_pb2.CancelOperationRequest, +): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + { + "name": "projects/sample1/instances/sample2/databases/sample3/operations/sample4" + }, + request, + ) + + # 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() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} + client.cancel_operation(request) + + +@pytest.mark.parametrize( + "request_type", + [ + operations_pb2.CancelOperationRequest, + dict, + ], +) +def test_cancel_operation_rest(request_type): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = { + "name": "projects/sample1/instances/sample2/databases/sample3/operations/sample4" + } + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "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.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} + + response = client.cancel_operation(request) + + # Establish that the response is the type that we expect. + assert response is None + + +def test_delete_operation_rest_bad_request( + request_type=operations_pb2.DeleteOperationRequest, +): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + { + "name": "projects/sample1/instances/sample2/databases/sample3/operations/sample4" + }, + request, + ) + + # 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() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} + client.delete_operation(request) + + +@pytest.mark.parametrize( + "request_type", + [ + operations_pb2.DeleteOperationRequest, + dict, + ], +) +def test_delete_operation_rest(request_type): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = { + "name": "projects/sample1/instances/sample2/databases/sample3/operations/sample4" + } + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "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.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} + + response = client.delete_operation(request) + + # Establish that the response is the type that we expect. + assert response is None + + +def test_get_operation_rest_bad_request( + request_type=operations_pb2.GetOperationRequest, +): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + { + "name": "projects/sample1/instances/sample2/databases/sample3/operations/sample4" + }, + request, + ) + + # 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() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} + client.get_operation(request) + + +@pytest.mark.parametrize( + "request_type", + [ + operations_pb2.GetOperationRequest, + dict, + ], +) +def test_get_operation_rest(request_type): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = { + "name": "projects/sample1/instances/sample2/databases/sample3/operations/sample4" + } + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "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.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} + + response = client.get_operation(request) + + # Establish that the response is the type that we expect. + assert isinstance(response, operations_pb2.Operation) + + +def test_list_operations_rest_bad_request( + request_type=operations_pb2.ListOperationsRequest, +): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + request = request_type() + request = json_format.ParseDict( + {"name": "projects/sample1/instances/sample2/databases/sample3/operations"}, + request, + ) + + # 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() + json_return_value = "" + response_value.json = mock.Mock(return_value={}) + response_value.status_code = 400 + response_value.request = Request() + req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} + client.list_operations(request) + + +@pytest.mark.parametrize( + "request_type", + [ + operations_pb2.ListOperationsRequest, + dict, + ], +) +def test_list_operations_rest(request_type): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + transport="rest", + ) + + request_init = { + "name": "projects/sample1/instances/sample2/databases/sample3/operations" + } + request = request_type(**request_init) + # Mock the http request call within the method and fake a response. + with mock.patch.object(Session, "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.content = json_return_value.encode("UTF-8") + + req.return_value = response_value + req.return_value.headers = {"header-1": "value-1", "header-2": "value-2"} + + response = 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(): client = InstanceAdminClient( credentials=ga_credentials.AnonymousCredentials(), transport="rest" @@ -18198,6 +18465,10 @@ def test_instance_admin_base_transport(): "update_instance_partition", "list_instance_partition_operations", "move_instance", + "get_operation", + "cancel_operation", + "delete_operation", + "list_operations", ) for method in methods: with pytest.raises(NotImplementedError): @@ -18896,6 +19167,574 @@ def test_client_with_default_client_info(): prep.assert_called_once_with(client_info) +def test_delete_operation(transport: str = "grpc"): + client = InstanceAdminClient( + 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 = operations_pb2.DeleteOperationRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + response = client.delete_operation(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 response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_delete_operation_async(transport: str = "grpc_asyncio"): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + 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 = operations_pb2.DeleteOperationRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + response = await client.delete_operation(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 response is the type that we expect. + assert response is None + + +def test_delete_operation_field_headers(): + client = InstanceAdminClient( + 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 = operations_pb2.DeleteOperationRequest() + request.name = "locations" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_operation), "__call__") as call: + call.return_value = None + + client.delete_operation(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=locations", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_delete_operation_field_headers_async(): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + ) + + # 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 = operations_pb2.DeleteOperationRequest() + request.name = "locations" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_operation), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + await client.delete_operation(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=locations", + ) in kw["metadata"] + + +def test_delete_operation_from_dict(): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.delete_operation( + request={ + "name": "locations", + } + ) + call.assert_called() + + +@pytest.mark.asyncio +async def test_delete_operation_from_dict_async(): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + ) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + response = await client.delete_operation( + request={ + "name": "locations", + } + ) + call.assert_called() + + +def test_cancel_operation(transport: str = "grpc"): + client = InstanceAdminClient( + 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 = operations_pb2.CancelOperationRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.cancel_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + response = client.cancel_operation(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 response is the type that we expect. + assert response is None + + +@pytest.mark.asyncio +async def test_cancel_operation_async(transport: str = "grpc_asyncio"): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + 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 = operations_pb2.CancelOperationRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.cancel_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + response = await client.cancel_operation(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 response is the type that we expect. + assert response is None + + +def test_cancel_operation_field_headers(): + client = InstanceAdminClient( + 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 = operations_pb2.CancelOperationRequest() + request.name = "locations" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.cancel_operation), "__call__") as call: + call.return_value = None + + client.cancel_operation(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=locations", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_cancel_operation_field_headers_async(): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + ) + + # 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 = operations_pb2.CancelOperationRequest() + request.name = "locations" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.cancel_operation), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + await client.cancel_operation(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=locations", + ) in kw["metadata"] + + +def test_cancel_operation_from_dict(): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.cancel_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = None + + response = client.cancel_operation( + request={ + "name": "locations", + } + ) + call.assert_called() + + +@pytest.mark.asyncio +async def test_cancel_operation_from_dict_async(): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + ) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.cancel_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall(None) + response = await client.cancel_operation( + request={ + "name": "locations", + } + ) + call.assert_called() + + +def test_get_operation(transport: str = "grpc"): + client = InstanceAdminClient( + 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 = operations_pb2.GetOperationRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation() + response = client.get_operation(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 response is the type that we expect. + assert isinstance(response, operations_pb2.Operation) + + +@pytest.mark.asyncio +async def test_get_operation_async(transport: str = "grpc_asyncio"): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + 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 = operations_pb2.GetOperationRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation() + ) + response = await client.get_operation(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 response is the type that we expect. + assert isinstance(response, operations_pb2.Operation) + + +def test_get_operation_field_headers(): + client = InstanceAdminClient( + 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 = operations_pb2.GetOperationRequest() + request.name = "locations" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + call.return_value = operations_pb2.Operation() + + client.get_operation(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=locations", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_operation_field_headers_async(): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + ) + + # 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 = operations_pb2.GetOperationRequest() + request.name = "locations" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation() + ) + await client.get_operation(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=locations", + ) in kw["metadata"] + + +def test_get_operation_from_dict(): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation() + + response = client.get_operation( + request={ + "name": "locations", + } + ) + call.assert_called() + + +@pytest.mark.asyncio +async def test_get_operation_from_dict_async(): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + ) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation() + ) + response = await client.get_operation( + request={ + "name": "locations", + } + ) + call.assert_called() + + +def test_list_operations(transport: str = "grpc"): + client = InstanceAdminClient( + 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 = operations_pb2.ListOperationsRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_operations), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.ListOperationsResponse() + response = client.list_operations(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 response is the type that we expect. + assert isinstance(response, operations_pb2.ListOperationsResponse) + + +@pytest.mark.asyncio +async def test_list_operations_async(transport: str = "grpc_asyncio"): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + 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 = operations_pb2.ListOperationsRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_operations), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.ListOperationsResponse() + ) + response = await client.list_operations(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 response is the type that we expect. + assert isinstance(response, operations_pb2.ListOperationsResponse) + + +def test_list_operations_field_headers(): + client = InstanceAdminClient( + 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 = operations_pb2.ListOperationsRequest() + request.name = "locations" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_operations), "__call__") as call: + call.return_value = operations_pb2.ListOperationsResponse() + + client.list_operations(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=locations", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_list_operations_field_headers_async(): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + ) + + # 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 = operations_pb2.ListOperationsRequest() + request.name = "locations" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_operations), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.ListOperationsResponse() + ) + await client.list_operations(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=locations", + ) in kw["metadata"] + + +def test_list_operations_from_dict(): + client = InstanceAdminClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_operations), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.ListOperationsResponse() + + response = client.list_operations( + request={ + "name": "locations", + } + ) + call.assert_called() + + +@pytest.mark.asyncio +async def test_list_operations_from_dict_async(): + client = InstanceAdminAsyncClient( + credentials=async_anonymous_credentials(), + ) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_operations), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.ListOperationsResponse() + ) + response = await client.list_operations( + request={ + "name": "locations", + } + ) + call.assert_called() + + def test_transport_close_grpc(): client = InstanceAdminClient( credentials=ga_credentials.AnonymousCredentials(), transport="grpc"