diff --git a/google/cloud/secretmanager/__init__.py b/google/cloud/secretmanager/__init__.py index 367a974..2e898cb 100644 --- a/google/cloud/secretmanager/__init__.py +++ b/google/cloud/secretmanager/__init__.py @@ -30,6 +30,7 @@ from google.cloud.secretmanager_v1.types.resources import Secret from google.cloud.secretmanager_v1.types.resources import SecretPayload from google.cloud.secretmanager_v1.types.resources import SecretVersion +from google.cloud.secretmanager_v1.types.resources import Topic from google.cloud.secretmanager_v1.types.service import AccessSecretVersionRequest from google.cloud.secretmanager_v1.types.service import AccessSecretVersionResponse from google.cloud.secretmanager_v1.types.service import AddSecretVersionRequest @@ -70,5 +71,6 @@ "SecretManagerServiceClient", "SecretPayload", "SecretVersion", + "Topic", "UpdateSecretRequest", ) diff --git a/google/cloud/secretmanager_v1/__init__.py b/google/cloud/secretmanager_v1/__init__.py index f0bc5f9..65f29c5 100644 --- a/google/cloud/secretmanager_v1/__init__.py +++ b/google/cloud/secretmanager_v1/__init__.py @@ -23,6 +23,7 @@ from .types.resources import Secret from .types.resources import SecretPayload from .types.resources import SecretVersion +from .types.resources import Topic from .types.service import AccessSecretVersionRequest from .types.service import AccessSecretVersionResponse from .types.service import AddSecretVersionRequest @@ -62,6 +63,7 @@ "Secret", "SecretPayload", "SecretVersion", + "Topic", "UpdateSecretRequest", "SecretManagerServiceClient", ) diff --git a/google/cloud/secretmanager_v1/services/secret_manager_service/async_client.py b/google/cloud/secretmanager_v1/services/secret_manager_service/async_client.py index f0020b2..3ff45a1 100644 --- a/google/cloud/secretmanager_v1/services/secret_manager_service/async_client.py +++ b/google/cloud/secretmanager_v1/services/secret_manager_service/async_client.py @@ -63,6 +63,8 @@ class SecretManagerServiceAsyncClient: parse_secret_version_path = staticmethod( SecretManagerServiceClient.parse_secret_version_path ) + topic_path = staticmethod(SecretManagerServiceClient.topic_path) + parse_topic_path = staticmethod(SecretManagerServiceClient.parse_topic_path) common_billing_account_path = staticmethod( SecretManagerServiceClient.common_billing_account_path @@ -93,8 +95,36 @@ class SecretManagerServiceAsyncClient: SecretManagerServiceClient.parse_common_location_path ) - from_service_account_info = SecretManagerServiceClient.from_service_account_info - from_service_account_file = SecretManagerServiceClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SecretManagerServiceAsyncClient: The constructed client. + """ + return SecretManagerServiceClient.from_service_account_info.__func__(SecretManagerServiceAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SecretManagerServiceAsyncClient: The constructed client. + """ + return SecretManagerServiceClient.from_service_account_file.__func__(SecretManagerServiceAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property diff --git a/google/cloud/secretmanager_v1/services/secret_manager_service/client.py b/google/cloud/secretmanager_v1/services/secret_manager_service/client.py index e56e5e4..a1eedea 100644 --- a/google/cloud/secretmanager_v1/services/secret_manager_service/client.py +++ b/google/cloud/secretmanager_v1/services/secret_manager_service/client.py @@ -199,6 +199,17 @@ def parse_secret_version_path(path: str) -> Dict[str, str]: ) return m.groupdict() if m else {} + @staticmethod + def topic_path(project: str, topic: str,) -> str: + """Return a fully-qualified topic string.""" + return "projects/{project}/topics/{topic}".format(project=project, topic=topic,) + + @staticmethod + def parse_topic_path(path: str) -> Dict[str, str]: + """Parse a topic path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/topics/(?P.+?)$", path) + return m.groupdict() if m else {} + @staticmethod def common_billing_account_path(billing_account: str,) -> str: """Return a fully-qualified billing_account string.""" @@ -313,21 +324,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -370,7 +377,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -1488,10 +1495,13 @@ def set_iam_policy( """ # 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): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. request = iam_policy.SetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy.SetIamPolicyRequest() # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. @@ -1593,10 +1603,13 @@ def get_iam_policy( """ # 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): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. request = iam_policy.GetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy.GetIamPolicyRequest() # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. @@ -1648,10 +1661,13 @@ def test_iam_permissions( """ # 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): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. request = iam_policy.TestIamPermissionsRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy.TestIamPermissionsRequest() # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. diff --git a/google/cloud/secretmanager_v1/services/secret_manager_service/pagers.py b/google/cloud/secretmanager_v1/services/secret_manager_service/pagers.py index db5a2a1..eda8432 100644 --- a/google/cloud/secretmanager_v1/services/secret_manager_service/pagers.py +++ b/google/cloud/secretmanager_v1/services/secret_manager_service/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.secretmanager_v1.types import resources from google.cloud.secretmanager_v1.types import service diff --git a/google/cloud/secretmanager_v1/services/secret_manager_service/transports/grpc.py b/google/cloud/secretmanager_v1/services/secret_manager_service/transports/grpc.py index 616cbe9..7c71715 100644 --- a/google/cloud/secretmanager_v1/services/secret_manager_service/transports/grpc.py +++ b/google/cloud/secretmanager_v1/services/secret_manager_service/transports/grpc.py @@ -67,6 +67,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -97,6 +98,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -113,6 +118,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -122,11 +132,6 @@ def __init__( self._grpc_channel = channel self._ssl_channel_credentials = None elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -170,12 +175,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/secretmanager_v1/services/secret_manager_service/transports/grpc_asyncio.py b/google/cloud/secretmanager_v1/services/secret_manager_service/transports/grpc_asyncio.py index 14d7021..21bcdc7 100644 --- a/google/cloud/secretmanager_v1/services/secret_manager_service/transports/grpc_asyncio.py +++ b/google/cloud/secretmanager_v1/services/secret_manager_service/transports/grpc_asyncio.py @@ -111,6 +111,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -142,6 +143,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -158,6 +163,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -167,11 +177,6 @@ def __init__( self._grpc_channel = channel self._ssl_channel_credentials = None elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -215,12 +220,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/secretmanager_v1/types/__init__.py b/google/cloud/secretmanager_v1/types/__init__.py index 3fe1200..20841fc 100644 --- a/google/cloud/secretmanager_v1/types/__init__.py +++ b/google/cloud/secretmanager_v1/types/__init__.py @@ -16,53 +16,55 @@ # from .resources import ( - Secret, - SecretVersion, - Replication, CustomerManagedEncryption, - ReplicationStatus, CustomerManagedEncryptionStatus, + Replication, + ReplicationStatus, + Secret, SecretPayload, + SecretVersion, + Topic, ) from .service import ( - ListSecretsRequest, - ListSecretsResponse, - CreateSecretRequest, - AddSecretVersionRequest, - GetSecretRequest, - ListSecretVersionsRequest, - ListSecretVersionsResponse, - GetSecretVersionRequest, - UpdateSecretRequest, AccessSecretVersionRequest, AccessSecretVersionResponse, + AddSecretVersionRequest, + CreateSecretRequest, DeleteSecretRequest, + DestroySecretVersionRequest, DisableSecretVersionRequest, EnableSecretVersionRequest, - DestroySecretVersionRequest, + GetSecretRequest, + GetSecretVersionRequest, + ListSecretsRequest, + ListSecretsResponse, + ListSecretVersionsRequest, + ListSecretVersionsResponse, + UpdateSecretRequest, ) __all__ = ( - "Secret", - "SecretVersion", - "Replication", "CustomerManagedEncryption", - "ReplicationStatus", "CustomerManagedEncryptionStatus", + "Replication", + "ReplicationStatus", + "Secret", "SecretPayload", - "ListSecretsRequest", - "ListSecretsResponse", - "CreateSecretRequest", - "AddSecretVersionRequest", - "GetSecretRequest", - "ListSecretVersionsRequest", - "ListSecretVersionsResponse", - "GetSecretVersionRequest", - "UpdateSecretRequest", + "SecretVersion", + "Topic", "AccessSecretVersionRequest", "AccessSecretVersionResponse", + "AddSecretVersionRequest", + "CreateSecretRequest", "DeleteSecretRequest", + "DestroySecretVersionRequest", "DisableSecretVersionRequest", "EnableSecretVersionRequest", - "DestroySecretVersionRequest", + "GetSecretRequest", + "GetSecretVersionRequest", + "ListSecretsRequest", + "ListSecretsResponse", + "ListSecretVersionsRequest", + "ListSecretVersionsResponse", + "UpdateSecretRequest", ) diff --git a/google/cloud/secretmanager_v1/types/resources.py b/google/cloud/secretmanager_v1/types/resources.py index 23b45cd..d782182 100644 --- a/google/cloud/secretmanager_v1/types/resources.py +++ b/google/cloud/secretmanager_v1/types/resources.py @@ -31,6 +31,7 @@ "CustomerManagedEncryption", "ReplicationStatus", "CustomerManagedEncryptionStatus", + "Topic", "SecretPayload", }, ) @@ -74,6 +75,11 @@ class Secret(proto.Message): ``[\p{Ll}\p{Lo}\p{N}_-]{0,63}`` No more than 64 labels can be assigned to a given resource. + topics (Sequence[google.cloud.secretmanager_v1.types.Topic]): + Optional. A list of up to 10 Pub/Sub topics + to which messages are published when control + plane operations are called on the secret or its + versions. expire_time (google.protobuf.timestamp_pb2.Timestamp): Optional. Timestamp in UTC when the [Secret][google.cloud.secretmanager.v1.Secret] is scheduled @@ -92,6 +98,8 @@ class Secret(proto.Message): labels = proto.MapField(proto.STRING, proto.STRING, number=4) + topics = proto.RepeatedField(proto.MESSAGE, number=5, message="Topic",) + expire_time = proto.Field( proto.MESSAGE, number=6, oneof="expiration", message=timestamp.Timestamp, ) @@ -383,6 +391,22 @@ class CustomerManagedEncryptionStatus(proto.Message): kms_key_version_name = proto.Field(proto.STRING, number=1) +class Topic(proto.Message): + r"""A Pub/Sub topic which Secret Manager will publish to when + control plane events occur on this secret. + + Attributes: + name (str): + Required. The resource name of the Pub/Sub topic that will + be published to, in the following format: + ``projects/*/topics/*``. For publication to succeed, the + Secret Manager P4SA must have ``pubsub.publisher`` + permissions on the topic. + """ + + name = proto.Field(proto.STRING, number=1) + + class SecretPayload(proto.Message): r"""A secret payload resource in the Secret Manager API. This contains the sensitive secret payload that is associated with a diff --git a/google/cloud/secretmanager_v1beta1/services/secret_manager_service/async_client.py b/google/cloud/secretmanager_v1beta1/services/secret_manager_service/async_client.py index 96f04dd..a0c2218 100644 --- a/google/cloud/secretmanager_v1beta1/services/secret_manager_service/async_client.py +++ b/google/cloud/secretmanager_v1beta1/services/secret_manager_service/async_client.py @@ -92,8 +92,36 @@ class SecretManagerServiceAsyncClient: SecretManagerServiceClient.parse_common_location_path ) - from_service_account_info = SecretManagerServiceClient.from_service_account_info - from_service_account_file = SecretManagerServiceClient.from_service_account_file + @classmethod + def from_service_account_info(cls, info: dict, *args, **kwargs): + """Creates an instance of this client using the provided credentials info. + + Args: + info (dict): The service account private key info. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SecretManagerServiceAsyncClient: The constructed client. + """ + return SecretManagerServiceClient.from_service_account_info.__func__(SecretManagerServiceAsyncClient, info, *args, **kwargs) # type: ignore + + @classmethod + def from_service_account_file(cls, filename: str, *args, **kwargs): + """Creates an instance of this client using the provided credentials + file. + + Args: + filename (str): The path to the service account private key json + file. + args: Additional arguments to pass to the constructor. + kwargs: Additional arguments to pass to the constructor. + + Returns: + SecretManagerServiceAsyncClient: The constructed client. + """ + return SecretManagerServiceClient.from_service_account_file.__func__(SecretManagerServiceAsyncClient, filename, *args, **kwargs) # type: ignore + from_service_account_json = from_service_account_file @property diff --git a/google/cloud/secretmanager_v1beta1/services/secret_manager_service/client.py b/google/cloud/secretmanager_v1beta1/services/secret_manager_service/client.py index 5d3a912..e2ff7c5 100644 --- a/google/cloud/secretmanager_v1beta1/services/secret_manager_service/client.py +++ b/google/cloud/secretmanager_v1beta1/services/secret_manager_service/client.py @@ -312,21 +312,17 @@ def __init__( util.strtobool(os.getenv("GOOGLE_API_USE_CLIENT_CERTIFICATE", "false")) ) - ssl_credentials = None + client_cert_source_func = None is_mtls = False if use_client_cert: if client_options.client_cert_source: - import grpc # type: ignore - - cert, key = client_options.client_cert_source() - ssl_credentials = grpc.ssl_channel_credentials( - certificate_chain=cert, private_key=key - ) is_mtls = True + client_cert_source_func = client_options.client_cert_source else: - creds = SslCredentials() - is_mtls = creds.is_mtls - ssl_credentials = creds.ssl_credentials if is_mtls else None + is_mtls = mtls.has_default_client_cert_source() + client_cert_source_func = ( + mtls.default_client_cert_source() if is_mtls else None + ) # Figure out which api endpoint to use. if client_options.api_endpoint is not None: @@ -369,7 +365,7 @@ def __init__( credentials_file=client_options.credentials_file, host=api_endpoint, scopes=client_options.scopes, - ssl_channel_credentials=ssl_credentials, + client_cert_source_for_mtls=client_cert_source_func, quota_project_id=client_options.quota_project_id, client_info=client_info, ) @@ -1483,10 +1479,13 @@ def set_iam_policy( """ # 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): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. request = iam_policy.SetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy.SetIamPolicyRequest() # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. @@ -1588,10 +1587,13 @@ def get_iam_policy( """ # 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): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. request = iam_policy.GetIamPolicyRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy.GetIamPolicyRequest() # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. @@ -1643,10 +1645,13 @@ def test_iam_permissions( """ # 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): + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. request = iam_policy.TestIamPermissionsRequest(**request) + elif not request: + # Null request, just make one. + request = iam_policy.TestIamPermissionsRequest() # Wrap the RPC method; this adds retry and timeout information, # and friendly error handling. diff --git a/google/cloud/secretmanager_v1beta1/services/secret_manager_service/pagers.py b/google/cloud/secretmanager_v1beta1/services/secret_manager_service/pagers.py index 32f4866..67d87f2 100644 --- a/google/cloud/secretmanager_v1beta1/services/secret_manager_service/pagers.py +++ b/google/cloud/secretmanager_v1beta1/services/secret_manager_service/pagers.py @@ -15,7 +15,16 @@ # limitations under the License. # -from typing import Any, AsyncIterable, Awaitable, Callable, Iterable, Sequence, Tuple +from typing import ( + Any, + AsyncIterable, + Awaitable, + Callable, + Iterable, + Sequence, + Tuple, + Optional, +) from google.cloud.secretmanager_v1beta1.types import resources from google.cloud.secretmanager_v1beta1.types import service diff --git a/google/cloud/secretmanager_v1beta1/services/secret_manager_service/transports/grpc.py b/google/cloud/secretmanager_v1beta1/services/secret_manager_service/transports/grpc.py index e8ff977..3f82138 100644 --- a/google/cloud/secretmanager_v1beta1/services/secret_manager_service/transports/grpc.py +++ b/google/cloud/secretmanager_v1beta1/services/secret_manager_service/transports/grpc.py @@ -67,6 +67,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id: Optional[str] = None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -97,6 +98,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -113,6 +118,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -122,11 +132,6 @@ def __init__( self._grpc_channel = channel self._ssl_channel_credentials = None elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -170,12 +175,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/secretmanager_v1beta1/services/secret_manager_service/transports/grpc_asyncio.py b/google/cloud/secretmanager_v1beta1/services/secret_manager_service/transports/grpc_asyncio.py index c9a76ec..c525386 100644 --- a/google/cloud/secretmanager_v1beta1/services/secret_manager_service/transports/grpc_asyncio.py +++ b/google/cloud/secretmanager_v1beta1/services/secret_manager_service/transports/grpc_asyncio.py @@ -111,6 +111,7 @@ def __init__( api_mtls_endpoint: str = None, client_cert_source: Callable[[], Tuple[bytes, bytes]] = None, ssl_channel_credentials: grpc.ChannelCredentials = None, + client_cert_source_for_mtls: Callable[[], Tuple[bytes, bytes]] = None, quota_project_id=None, client_info: gapic_v1.client_info.ClientInfo = DEFAULT_CLIENT_INFO, ) -> None: @@ -142,6 +143,10 @@ def __init__( ``api_mtls_endpoint`` is None. ssl_channel_credentials (grpc.ChannelCredentials): SSL credentials for grpc channel. It is ignored if ``channel`` is provided. + client_cert_source_for_mtls (Optional[Callable[[], Tuple[bytes, bytes]]]): + A callback to provide client certificate bytes and private key bytes, + both in PEM format. It is used to configure mutual TLS channel. It is + ignored if ``channel`` or ``ssl_channel_credentials`` is provided. quota_project_id (Optional[str]): An optional project to use for billing and quota. client_info (google.api_core.gapic_v1.client_info.ClientInfo): @@ -158,6 +163,11 @@ def __init__( """ self._ssl_channel_credentials = ssl_channel_credentials + if api_mtls_endpoint: + warnings.warn("api_mtls_endpoint is deprecated", DeprecationWarning) + if client_cert_source: + warnings.warn("client_cert_source is deprecated", DeprecationWarning) + if channel: # Sanity check: Ensure that channel and credentials are not both # provided. @@ -167,11 +177,6 @@ def __init__( self._grpc_channel = channel self._ssl_channel_credentials = None elif api_mtls_endpoint: - warnings.warn( - "api_mtls_endpoint and client_cert_source are deprecated", - DeprecationWarning, - ) - host = ( api_mtls_endpoint if ":" in api_mtls_endpoint @@ -215,12 +220,18 @@ def __init__( scopes=self.AUTH_SCOPES, quota_project_id=quota_project_id ) + if client_cert_source_for_mtls and not ssl_channel_credentials: + cert, key = client_cert_source_for_mtls() + self._ssl_channel_credentials = grpc.ssl_channel_credentials( + certificate_chain=cert, private_key=key + ) + # create a new channel. The provided one is ignored. self._grpc_channel = type(self).create_channel( host, credentials=credentials, credentials_file=credentials_file, - ssl_credentials=ssl_channel_credentials, + ssl_credentials=self._ssl_channel_credentials, scopes=scopes or self.AUTH_SCOPES, quota_project_id=quota_project_id, options=[ diff --git a/google/cloud/secretmanager_v1beta1/types/__init__.py b/google/cloud/secretmanager_v1beta1/types/__init__.py index c287f6d..8cce13e 100644 --- a/google/cloud/secretmanager_v1beta1/types/__init__.py +++ b/google/cloud/secretmanager_v1beta1/types/__init__.py @@ -16,47 +16,47 @@ # from .resources import ( - Secret, - SecretVersion, Replication, + Secret, SecretPayload, + SecretVersion, ) from .service import ( - ListSecretsRequest, - ListSecretsResponse, - CreateSecretRequest, - AddSecretVersionRequest, - GetSecretRequest, - ListSecretVersionsRequest, - ListSecretVersionsResponse, - GetSecretVersionRequest, - UpdateSecretRequest, AccessSecretVersionRequest, AccessSecretVersionResponse, + AddSecretVersionRequest, + CreateSecretRequest, DeleteSecretRequest, + DestroySecretVersionRequest, DisableSecretVersionRequest, EnableSecretVersionRequest, - DestroySecretVersionRequest, + GetSecretRequest, + GetSecretVersionRequest, + ListSecretsRequest, + ListSecretsResponse, + ListSecretVersionsRequest, + ListSecretVersionsResponse, + UpdateSecretRequest, ) __all__ = ( - "Secret", - "SecretVersion", "Replication", + "Secret", "SecretPayload", - "ListSecretsRequest", - "ListSecretsResponse", - "CreateSecretRequest", - "AddSecretVersionRequest", - "GetSecretRequest", - "ListSecretVersionsRequest", - "ListSecretVersionsResponse", - "GetSecretVersionRequest", - "UpdateSecretRequest", + "SecretVersion", "AccessSecretVersionRequest", "AccessSecretVersionResponse", + "AddSecretVersionRequest", + "CreateSecretRequest", "DeleteSecretRequest", + "DestroySecretVersionRequest", "DisableSecretVersionRequest", "EnableSecretVersionRequest", - "DestroySecretVersionRequest", + "GetSecretRequest", + "GetSecretVersionRequest", + "ListSecretsRequest", + "ListSecretsResponse", + "ListSecretVersionsRequest", + "ListSecretVersionsResponse", + "UpdateSecretRequest", ) diff --git a/synth.metadata b/synth.metadata index 827e050..05bb993 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,15 +4,15 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/python-secret-manager.git", - "sha": "964c79e4c7006b7bfb64019635aa643964b65bab" + "sha": "0e29327d99f9774e6b7a942d8ba462d007b68bd4" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "9ecdacc9a00e1dd443b11bf10215d6e7648db8a7", - "internalRef": "352563582" + "sha": "28a591963253d52ce3a25a918cafbdd9928de8cf", + "internalRef": "361662015" } }, { diff --git a/tests/unit/gapic/secretmanager_v1/__init__.py b/tests/unit/gapic/secretmanager_v1/__init__.py index 8b13789..42ffdf2 100644 --- a/tests/unit/gapic/secretmanager_v1/__init__.py +++ b/tests/unit/gapic/secretmanager_v1/__init__.py @@ -1 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 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. +# diff --git a/tests/unit/gapic/secretmanager_v1/test_secret_manager_service.py b/tests/unit/gapic/secretmanager_v1/test_secret_manager_service.py index f01241a..f884c30 100644 --- a/tests/unit/gapic/secretmanager_v1/test_secret_manager_service.py +++ b/tests/unit/gapic/secretmanager_v1/test_secret_manager_service.py @@ -97,15 +97,19 @@ def test__get_default_mtls_endpoint(): ) -def test_secret_manager_service_client_from_service_account_info(): +@pytest.mark.parametrize( + "client_class", [SecretManagerServiceClient, SecretManagerServiceAsyncClient,] +) +def test_secret_manager_service_client_from_service_account_info(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( service_account.Credentials, "from_service_account_info" ) as factory: factory.return_value = creds info = {"valid": True} - client = SecretManagerServiceClient.from_service_account_info(info) + client = client_class.from_service_account_info(info) assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "secretmanager.googleapis.com:443" @@ -121,9 +125,11 @@ def test_secret_manager_service_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "secretmanager.googleapis.com:443" @@ -188,7 +194,7 @@ def test_secret_manager_service_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -204,7 +210,7 @@ def test_secret_manager_service_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -220,7 +226,7 @@ def test_secret_manager_service_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -248,7 +254,7 @@ def test_secret_manager_service_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -309,29 +315,25 @@ def test_secret_manager_service_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -340,66 +342,53 @@ def test_secret_manager_service_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -429,7 +418,7 @@ def test_secret_manager_service_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -463,7 +452,7 @@ def test_secret_manager_service_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -482,7 +471,7 @@ def test_secret_manager_service_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -525,6 +514,22 @@ def test_list_secrets_from_dict(): test_list_secrets(request_type=dict) +def test_list_secrets_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_secrets), "__call__") as call: + client.list_secrets() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.ListSecretsRequest() + + @pytest.mark.asyncio async def test_list_secrets_async( transport: str = "grpc_asyncio", request_type=service.ListSecretsRequest @@ -854,6 +859,22 @@ def test_create_secret_from_dict(): test_create_secret(request_type=dict) +def test_create_secret_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_secret), "__call__") as call: + client.create_secret() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.CreateSecretRequest() + + @pytest.mark.asyncio async def test_create_secret_async( transport: str = "grpc_asyncio", request_type=service.CreateSecretRequest @@ -1075,6 +1096,24 @@ def test_add_secret_version_from_dict(): test_add_secret_version(request_type=dict) +def test_add_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.add_secret_version), "__call__" + ) as call: + client.add_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.AddSecretVersionRequest() + + @pytest.mark.asyncio async def test_add_secret_version_async( transport: str = "grpc_asyncio", request_type=service.AddSecretVersionRequest @@ -1298,6 +1337,22 @@ def test_get_secret_from_dict(): test_get_secret(request_type=dict) +def test_get_secret_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_secret), "__call__") as call: + client.get_secret() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.GetSecretRequest() + + @pytest.mark.asyncio async def test_get_secret_async( transport: str = "grpc_asyncio", request_type=service.GetSecretRequest @@ -1493,6 +1548,22 @@ def test_update_secret_from_dict(): test_update_secret(request_type=dict) +def test_update_secret_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_secret), "__call__") as call: + client.update_secret() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.UpdateSecretRequest() + + @pytest.mark.asyncio async def test_update_secret_async( transport: str = "grpc_asyncio", request_type=service.UpdateSecretRequest @@ -1697,6 +1768,22 @@ def test_delete_secret_from_dict(): test_delete_secret(request_type=dict) +def test_delete_secret_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_secret), "__call__") as call: + client.delete_secret() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.DeleteSecretRequest() + + @pytest.mark.asyncio async def test_delete_secret_async( transport: str = "grpc_asyncio", request_type=service.DeleteSecretRequest @@ -1892,6 +1979,24 @@ def test_list_secret_versions_from_dict(): test_list_secret_versions(request_type=dict) +def test_list_secret_versions_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_secret_versions), "__call__" + ) as call: + client.list_secret_versions() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.ListSecretVersionsRequest() + + @pytest.mark.asyncio async def test_list_secret_versions_async( transport: str = "grpc_asyncio", request_type=service.ListSecretVersionsRequest @@ -2259,6 +2364,24 @@ def test_get_secret_version_from_dict(): test_get_secret_version(request_type=dict) +def test_get_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_secret_version), "__call__" + ) as call: + client.get_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.GetSecretVersionRequest() + + @pytest.mark.asyncio async def test_get_secret_version_async( transport: str = "grpc_asyncio", request_type=service.GetSecretVersionRequest @@ -2472,6 +2595,24 @@ def test_access_secret_version_from_dict(): test_access_secret_version(request_type=dict) +def test_access_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.access_secret_version), "__call__" + ) as call: + client.access_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.AccessSecretVersionRequest() + + @pytest.mark.asyncio async def test_access_secret_version_async( transport: str = "grpc_asyncio", request_type=service.AccessSecretVersionRequest @@ -2685,6 +2826,24 @@ def test_disable_secret_version_from_dict(): test_disable_secret_version(request_type=dict) +def test_disable_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.disable_secret_version), "__call__" + ) as call: + client.disable_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.DisableSecretVersionRequest() + + @pytest.mark.asyncio async def test_disable_secret_version_async( transport: str = "grpc_asyncio", request_type=service.DisableSecretVersionRequest @@ -2902,6 +3061,24 @@ def test_enable_secret_version_from_dict(): test_enable_secret_version(request_type=dict) +def test_enable_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.enable_secret_version), "__call__" + ) as call: + client.enable_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.EnableSecretVersionRequest() + + @pytest.mark.asyncio async def test_enable_secret_version_async( transport: str = "grpc_asyncio", request_type=service.EnableSecretVersionRequest @@ -3119,6 +3296,24 @@ def test_destroy_secret_version_from_dict(): test_destroy_secret_version(request_type=dict) +def test_destroy_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.destroy_secret_version), "__call__" + ) as call: + client.destroy_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.DestroySecretVersionRequest() + + @pytest.mark.asyncio async def test_destroy_secret_version_async( transport: str = "grpc_asyncio", request_type=service.DestroySecretVersionRequest @@ -3332,6 +3527,22 @@ def test_set_iam_policy_from_dict(): test_set_iam_policy(request_type=dict) +def test_set_iam_policy_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + client.set_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == iam_policy.SetIamPolicyRequest() + + @pytest.mark.asyncio async def test_set_iam_policy_async( transport: str = "grpc_asyncio", request_type=iam_policy.SetIamPolicyRequest @@ -3476,6 +3687,22 @@ def test_get_iam_policy_from_dict(): test_get_iam_policy(request_type=dict) +def test_get_iam_policy_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + client.get_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == iam_policy.GetIamPolicyRequest() + + @pytest.mark.asyncio async def test_get_iam_policy_async( transport: str = "grpc_asyncio", request_type=iam_policy.GetIamPolicyRequest @@ -3622,6 +3849,24 @@ def test_test_iam_permissions_from_dict(): test_test_iam_permissions(request_type=dict) +def test_test_iam_permissions_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + client.test_iam_permissions() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == iam_policy.TestIamPermissionsRequest() + + @pytest.mark.asyncio async def test_test_iam_permissions_async( transport: str = "grpc_asyncio", request_type=iam_policy.TestIamPermissionsRequest @@ -3910,6 +4155,53 @@ def test_secret_manager_service_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.SecretManagerServiceGrpcTransport, + transports.SecretManagerServiceGrpcAsyncIOTransport, + ], +) +def test_secret_manager_service_grpc_transport_client_cert_source_for_mtls( + transport_class, +): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_secret_manager_service_host_no_port(): client = SecretManagerServiceClient( credentials=credentials.AnonymousCredentials(), @@ -3954,6 +4246,8 @@ def test_secret_manager_service_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [ @@ -4006,6 +4300,8 @@ def test_secret_manager_service_transport_channel_mtls_with_client_cert_source( assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [ @@ -4100,8 +4396,29 @@ def test_parse_secret_version_path(): assert expected == actual +def test_topic_path(): + project = "scallop" + topic = "abalone" + + expected = "projects/{project}/topics/{topic}".format(project=project, topic=topic,) + actual = SecretManagerServiceClient.topic_path(project, topic) + assert expected == actual + + +def test_parse_topic_path(): + expected = { + "project": "squid", + "topic": "clam", + } + path = SecretManagerServiceClient.topic_path(**expected) + + # Check that the path construction is reversible. + actual = SecretManagerServiceClient.parse_topic_path(path) + assert expected == actual + + def test_common_billing_account_path(): - billing_account = "scallop" + billing_account = "whelk" expected = "billingAccounts/{billing_account}".format( billing_account=billing_account, @@ -4112,7 +4429,7 @@ def test_common_billing_account_path(): def test_parse_common_billing_account_path(): expected = { - "billing_account": "abalone", + "billing_account": "octopus", } path = SecretManagerServiceClient.common_billing_account_path(**expected) @@ -4122,7 +4439,7 @@ def test_parse_common_billing_account_path(): def test_common_folder_path(): - folder = "squid" + folder = "oyster" expected = "folders/{folder}".format(folder=folder,) actual = SecretManagerServiceClient.common_folder_path(folder) @@ -4131,7 +4448,7 @@ def test_common_folder_path(): def test_parse_common_folder_path(): expected = { - "folder": "clam", + "folder": "nudibranch", } path = SecretManagerServiceClient.common_folder_path(**expected) @@ -4141,7 +4458,7 @@ def test_parse_common_folder_path(): def test_common_organization_path(): - organization = "whelk" + organization = "cuttlefish" expected = "organizations/{organization}".format(organization=organization,) actual = SecretManagerServiceClient.common_organization_path(organization) @@ -4150,7 +4467,7 @@ def test_common_organization_path(): def test_parse_common_organization_path(): expected = { - "organization": "octopus", + "organization": "mussel", } path = SecretManagerServiceClient.common_organization_path(**expected) @@ -4160,7 +4477,7 @@ def test_parse_common_organization_path(): def test_common_project_path(): - project = "oyster" + project = "winkle" expected = "projects/{project}".format(project=project,) actual = SecretManagerServiceClient.common_project_path(project) @@ -4169,7 +4486,7 @@ def test_common_project_path(): def test_parse_common_project_path(): expected = { - "project": "nudibranch", + "project": "nautilus", } path = SecretManagerServiceClient.common_project_path(**expected) @@ -4179,8 +4496,8 @@ def test_parse_common_project_path(): def test_common_location_path(): - project = "cuttlefish" - location = "mussel" + project = "scallop" + location = "abalone" expected = "projects/{project}/locations/{location}".format( project=project, location=location, @@ -4191,8 +4508,8 @@ def test_common_location_path(): def test_parse_common_location_path(): expected = { - "project": "winkle", - "location": "nautilus", + "project": "squid", + "location": "clam", } path = SecretManagerServiceClient.common_location_path(**expected) diff --git a/tests/unit/gapic/secretmanager_v1beta1/__init__.py b/tests/unit/gapic/secretmanager_v1beta1/__init__.py index 8b13789..42ffdf2 100644 --- a/tests/unit/gapic/secretmanager_v1beta1/__init__.py +++ b/tests/unit/gapic/secretmanager_v1beta1/__init__.py @@ -1 +1,16 @@ +# -*- coding: utf-8 -*- +# Copyright 2020 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. +# diff --git a/tests/unit/gapic/secretmanager_v1beta1/test_secret_manager_service.py b/tests/unit/gapic/secretmanager_v1beta1/test_secret_manager_service.py index 64bc147..a096d89 100644 --- a/tests/unit/gapic/secretmanager_v1beta1/test_secret_manager_service.py +++ b/tests/unit/gapic/secretmanager_v1beta1/test_secret_manager_service.py @@ -98,15 +98,19 @@ def test__get_default_mtls_endpoint(): ) -def test_secret_manager_service_client_from_service_account_info(): +@pytest.mark.parametrize( + "client_class", [SecretManagerServiceClient, SecretManagerServiceAsyncClient,] +) +def test_secret_manager_service_client_from_service_account_info(client_class): creds = credentials.AnonymousCredentials() with mock.patch.object( service_account.Credentials, "from_service_account_info" ) as factory: factory.return_value = creds info = {"valid": True} - client = SecretManagerServiceClient.from_service_account_info(info) + client = client_class.from_service_account_info(info) assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "secretmanager.googleapis.com:443" @@ -122,9 +126,11 @@ def test_secret_manager_service_client_from_service_account_file(client_class): factory.return_value = creds client = client_class.from_service_account_file("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) client = client_class.from_service_account_json("dummy/file/path.json") assert client.transport._credentials == creds + assert isinstance(client, client_class) assert client.transport._host == "secretmanager.googleapis.com:443" @@ -189,7 +195,7 @@ def test_secret_manager_service_client_client_options( credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -205,7 +211,7 @@ def test_secret_manager_service_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -221,7 +227,7 @@ def test_secret_manager_service_client_client_options( credentials_file=None, host=client.DEFAULT_MTLS_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -249,7 +255,7 @@ def test_secret_manager_service_client_client_options( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id="octopus", client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -310,29 +316,25 @@ def test_secret_manager_service_client_mtls_env_auto( client_cert_source=client_cert_source_callback ) with mock.patch.object(transport_class, "__init__") as patched: - ssl_channel_creds = mock.Mock() - with mock.patch( - "grpc.ssl_channel_credentials", return_value=ssl_channel_creds - ): - patched.return_value = None - client = client_class(client_options=options) + patched.return_value = None + client = client_class(client_options=options) - if use_client_cert_env == "false": - expected_ssl_channel_creds = None - expected_host = client.DEFAULT_ENDPOINT - else: - expected_ssl_channel_creds = ssl_channel_creds - expected_host = client.DEFAULT_MTLS_ENDPOINT + if use_client_cert_env == "false": + expected_client_cert_source = None + expected_host = client.DEFAULT_ENDPOINT + else: + expected_client_cert_source = client_cert_source_callback + expected_host = client.DEFAULT_MTLS_ENDPOINT - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=expected_host, + scopes=None, + client_cert_source_for_mtls=expected_client_cert_source, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) # Check the case ADC client cert is provided. Whether client cert is used depends on # GOOGLE_API_USE_CLIENT_CERTIFICATE value. @@ -341,66 +343,53 @@ def test_secret_manager_service_client_mtls_env_auto( ): with mock.patch.object(transport_class, "__init__") as patched: with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=True, ): with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.ssl_credentials", - new_callable=mock.PropertyMock, - ) as ssl_credentials_mock: - if use_client_cert_env == "false": - is_mtls_mock.return_value = False - ssl_credentials_mock.return_value = None - expected_host = client.DEFAULT_ENDPOINT - expected_ssl_channel_creds = None - else: - is_mtls_mock.return_value = True - ssl_credentials_mock.return_value = mock.Mock() - expected_host = client.DEFAULT_MTLS_ENDPOINT - expected_ssl_channel_creds = ( - ssl_credentials_mock.return_value - ) - - patched.return_value = None - client = client_class() - patched.assert_called_once_with( - credentials=None, - credentials_file=None, - host=expected_host, - scopes=None, - ssl_channel_credentials=expected_ssl_channel_creds, - quota_project_id=None, - client_info=transports.base.DEFAULT_CLIENT_INFO, - ) + "google.auth.transport.mtls.default_client_cert_source", + return_value=client_cert_source_callback, + ): + if use_client_cert_env == "false": + expected_host = client.DEFAULT_ENDPOINT + expected_client_cert_source = None + else: + expected_host = client.DEFAULT_MTLS_ENDPOINT + expected_client_cert_source = client_cert_source_callback - # Check the case client_cert_source and ADC client cert are not provided. - with mock.patch.dict( - os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} - ): - with mock.patch.object(transport_class, "__init__") as patched: - with mock.patch( - "google.auth.transport.grpc.SslCredentials.__init__", return_value=None - ): - with mock.patch( - "google.auth.transport.grpc.SslCredentials.is_mtls", - new_callable=mock.PropertyMock, - ) as is_mtls_mock: - is_mtls_mock.return_value = False patched.return_value = None client = client_class() patched.assert_called_once_with( credentials=None, credentials_file=None, - host=client.DEFAULT_ENDPOINT, + host=expected_host, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=expected_client_cert_source, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) + # Check the case client_cert_source and ADC client cert are not provided. + with mock.patch.dict( + os.environ, {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert_env} + ): + with mock.patch.object(transport_class, "__init__") as patched: + with mock.patch( + "google.auth.transport.mtls.has_default_client_cert_source", + return_value=False, + ): + patched.return_value = None + client = client_class() + patched.assert_called_once_with( + credentials=None, + credentials_file=None, + host=client.DEFAULT_ENDPOINT, + scopes=None, + client_cert_source_for_mtls=None, + quota_project_id=None, + client_info=transports.base.DEFAULT_CLIENT_INFO, + ) + @pytest.mark.parametrize( "client_class,transport_class,transport_name", @@ -430,7 +419,7 @@ def test_secret_manager_service_client_client_options_scopes( credentials_file=None, host=client.DEFAULT_ENDPOINT, scopes=["1", "2"], - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -464,7 +453,7 @@ def test_secret_manager_service_client_client_options_credentials_file( credentials_file="credentials.json", host=client.DEFAULT_ENDPOINT, scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -483,7 +472,7 @@ def test_secret_manager_service_client_client_options_from_dict(): credentials_file=None, host="squid.clam.whelk", scopes=None, - ssl_channel_credentials=None, + client_cert_source_for_mtls=None, quota_project_id=None, client_info=transports.base.DEFAULT_CLIENT_INFO, ) @@ -526,6 +515,22 @@ def test_list_secrets_from_dict(): test_list_secrets(request_type=dict) +def test_list_secrets_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_secrets), "__call__") as call: + client.list_secrets() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.ListSecretsRequest() + + @pytest.mark.asyncio async def test_list_secrets_async( transport: str = "grpc_asyncio", request_type=service.ListSecretsRequest @@ -853,6 +858,22 @@ def test_create_secret_from_dict(): test_create_secret(request_type=dict) +def test_create_secret_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.create_secret), "__call__") as call: + client.create_secret() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.CreateSecretRequest() + + @pytest.mark.asyncio async def test_create_secret_async( transport: str = "grpc_asyncio", request_type=service.CreateSecretRequest @@ -1074,6 +1095,24 @@ def test_add_secret_version_from_dict(): test_add_secret_version(request_type=dict) +def test_add_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.add_secret_version), "__call__" + ) as call: + client.add_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.AddSecretVersionRequest() + + @pytest.mark.asyncio async def test_add_secret_version_async( transport: str = "grpc_asyncio", request_type=service.AddSecretVersionRequest @@ -1295,6 +1334,22 @@ def test_get_secret_from_dict(): test_get_secret(request_type=dict) +def test_get_secret_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_secret), "__call__") as call: + client.get_secret() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.GetSecretRequest() + + @pytest.mark.asyncio async def test_get_secret_async( transport: str = "grpc_asyncio", request_type=service.GetSecretRequest @@ -1488,6 +1543,22 @@ def test_update_secret_from_dict(): test_update_secret(request_type=dict) +def test_update_secret_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.update_secret), "__call__") as call: + client.update_secret() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.UpdateSecretRequest() + + @pytest.mark.asyncio async def test_update_secret_async( transport: str = "grpc_asyncio", request_type=service.UpdateSecretRequest @@ -1692,6 +1763,22 @@ def test_delete_secret_from_dict(): test_delete_secret(request_type=dict) +def test_delete_secret_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.delete_secret), "__call__") as call: + client.delete_secret() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.DeleteSecretRequest() + + @pytest.mark.asyncio async def test_delete_secret_async( transport: str = "grpc_asyncio", request_type=service.DeleteSecretRequest @@ -1887,6 +1974,24 @@ def test_list_secret_versions_from_dict(): test_list_secret_versions(request_type=dict) +def test_list_secret_versions_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_secret_versions), "__call__" + ) as call: + client.list_secret_versions() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.ListSecretVersionsRequest() + + @pytest.mark.asyncio async def test_list_secret_versions_async( transport: str = "grpc_asyncio", request_type=service.ListSecretVersionsRequest @@ -2254,6 +2359,24 @@ def test_get_secret_version_from_dict(): test_get_secret_version(request_type=dict) +def test_get_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.get_secret_version), "__call__" + ) as call: + client.get_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.GetSecretVersionRequest() + + @pytest.mark.asyncio async def test_get_secret_version_async( transport: str = "grpc_asyncio", request_type=service.GetSecretVersionRequest @@ -2467,6 +2590,24 @@ def test_access_secret_version_from_dict(): test_access_secret_version(request_type=dict) +def test_access_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.access_secret_version), "__call__" + ) as call: + client.access_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.AccessSecretVersionRequest() + + @pytest.mark.asyncio async def test_access_secret_version_async( transport: str = "grpc_asyncio", request_type=service.AccessSecretVersionRequest @@ -2680,6 +2821,24 @@ def test_disable_secret_version_from_dict(): test_disable_secret_version(request_type=dict) +def test_disable_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.disable_secret_version), "__call__" + ) as call: + client.disable_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.DisableSecretVersionRequest() + + @pytest.mark.asyncio async def test_disable_secret_version_async( transport: str = "grpc_asyncio", request_type=service.DisableSecretVersionRequest @@ -2897,6 +3056,24 @@ def test_enable_secret_version_from_dict(): test_enable_secret_version(request_type=dict) +def test_enable_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.enable_secret_version), "__call__" + ) as call: + client.enable_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.EnableSecretVersionRequest() + + @pytest.mark.asyncio async def test_enable_secret_version_async( transport: str = "grpc_asyncio", request_type=service.EnableSecretVersionRequest @@ -3114,6 +3291,24 @@ def test_destroy_secret_version_from_dict(): test_destroy_secret_version(request_type=dict) +def test_destroy_secret_version_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.destroy_secret_version), "__call__" + ) as call: + client.destroy_secret_version() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == service.DestroySecretVersionRequest() + + @pytest.mark.asyncio async def test_destroy_secret_version_async( transport: str = "grpc_asyncio", request_type=service.DestroySecretVersionRequest @@ -3327,6 +3522,22 @@ def test_set_iam_policy_from_dict(): test_set_iam_policy(request_type=dict) +def test_set_iam_policy_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.set_iam_policy), "__call__") as call: + client.set_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == iam_policy.SetIamPolicyRequest() + + @pytest.mark.asyncio async def test_set_iam_policy_async( transport: str = "grpc_asyncio", request_type=iam_policy.SetIamPolicyRequest @@ -3471,6 +3682,22 @@ def test_get_iam_policy_from_dict(): test_get_iam_policy(request_type=dict) +def test_get_iam_policy_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_iam_policy), "__call__") as call: + client.get_iam_policy() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == iam_policy.GetIamPolicyRequest() + + @pytest.mark.asyncio async def test_get_iam_policy_async( transport: str = "grpc_asyncio", request_type=iam_policy.GetIamPolicyRequest @@ -3617,6 +3844,24 @@ def test_test_iam_permissions_from_dict(): test_test_iam_permissions(request_type=dict) +def test_test_iam_permissions_empty_call(): + # This test is a coverage failsafe to make sure that totally empty calls, + # i.e. request == None and no flattened fields passed, work. + client = SecretManagerServiceClient( + credentials=credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.test_iam_permissions), "__call__" + ) as call: + client.test_iam_permissions() + call.assert_called() + _, args, _ = call.mock_calls[0] + + assert args[0] == iam_policy.TestIamPermissionsRequest() + + @pytest.mark.asyncio async def test_test_iam_permissions_async( transport: str = "grpc_asyncio", request_type=iam_policy.TestIamPermissionsRequest @@ -3905,6 +4150,53 @@ def test_secret_manager_service_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.SecretManagerServiceGrpcTransport, + transports.SecretManagerServiceGrpcAsyncIOTransport, + ], +) +def test_secret_manager_service_grpc_transport_client_cert_source_for_mtls( + transport_class, +): + cred = credentials.AnonymousCredentials() + + # Check ssl_channel_credentials is used if provided. + with mock.patch.object(transport_class, "create_channel") as mock_create_channel: + mock_ssl_channel_creds = mock.Mock() + transport_class( + host="squid.clam.whelk", + credentials=cred, + ssl_channel_credentials=mock_ssl_channel_creds, + ) + mock_create_channel.assert_called_once_with( + "squid.clam.whelk:443", + credentials=cred, + credentials_file=None, + scopes=("https://www.googleapis.com/auth/cloud-platform",), + ssl_credentials=mock_ssl_channel_creds, + quota_project_id=None, + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + + # Check if ssl_channel_credentials is not provided, then client_cert_source_for_mtls + # is used. + with mock.patch.object(transport_class, "create_channel", return_value=mock.Mock()): + with mock.patch("grpc.ssl_channel_credentials") as mock_ssl_cred: + transport_class( + credentials=cred, + client_cert_source_for_mtls=client_cert_source_callback, + ) + expected_cert, expected_key = client_cert_source_callback() + mock_ssl_cred.assert_called_once_with( + certificate_chain=expected_cert, private_key=expected_key + ) + + def test_secret_manager_service_host_no_port(): client = SecretManagerServiceClient( credentials=credentials.AnonymousCredentials(), @@ -3949,6 +4241,8 @@ def test_secret_manager_service_grpc_asyncio_transport_channel(): assert transport._ssl_channel_credentials == None +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [ @@ -4001,6 +4295,8 @@ def test_secret_manager_service_transport_channel_mtls_with_client_cert_source( assert transport._ssl_channel_credentials == mock_ssl_cred +# Remove this test when deprecated arguments (api_mtls_endpoint, client_cert_source) are +# removed from grpc/grpc_asyncio transport constructor. @pytest.mark.parametrize( "transport_class", [