diff --git a/google/cloud/recaptchaenterprise/__init__.py b/google/cloud/recaptchaenterprise/__init__.py index 08a4a23..3efc980 100644 --- a/google/cloud/recaptchaenterprise/__init__.py +++ b/google/cloud/recaptchaenterprise/__init__.py @@ -31,6 +31,9 @@ AnnotateAssessmentResponse, ) from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import Assessment +from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import ( + ChallengeMetrics, +) from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import ( CreateAssessmentRequest, ) @@ -42,6 +45,9 @@ ) from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import Event from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import GetKeyRequest +from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import ( + GetMetricsRequest, +) from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import IOSKeySettings from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import Key from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import ( @@ -50,7 +56,16 @@ from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import ( ListKeysResponse, ) +from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import Metrics +from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import ( + MigrateKeyRequest, +) from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import RiskAnalysis +from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import ( + ScoreDistribution, +) +from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import ScoreMetrics +from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import TestingOptions from google.cloud.recaptchaenterprise_v1.types.recaptchaenterprise import ( TokenProperties, ) @@ -66,16 +81,23 @@ "AnnotateAssessmentRequest", "AnnotateAssessmentResponse", "Assessment", + "ChallengeMetrics", "CreateAssessmentRequest", "CreateKeyRequest", "DeleteKeyRequest", "Event", "GetKeyRequest", + "GetMetricsRequest", "IOSKeySettings", "Key", "ListKeysRequest", "ListKeysResponse", + "Metrics", + "MigrateKeyRequest", "RiskAnalysis", + "ScoreDistribution", + "ScoreMetrics", + "TestingOptions", "TokenProperties", "UpdateKeyRequest", "WebKeySettings", diff --git a/google/cloud/recaptchaenterprise_v1/__init__.py b/google/cloud/recaptchaenterprise_v1/__init__.py index 77340e0..b98676d 100644 --- a/google/cloud/recaptchaenterprise_v1/__init__.py +++ b/google/cloud/recaptchaenterprise_v1/__init__.py @@ -21,16 +21,23 @@ from .types.recaptchaenterprise import AnnotateAssessmentRequest from .types.recaptchaenterprise import AnnotateAssessmentResponse from .types.recaptchaenterprise import Assessment +from .types.recaptchaenterprise import ChallengeMetrics from .types.recaptchaenterprise import CreateAssessmentRequest from .types.recaptchaenterprise import CreateKeyRequest from .types.recaptchaenterprise import DeleteKeyRequest from .types.recaptchaenterprise import Event from .types.recaptchaenterprise import GetKeyRequest +from .types.recaptchaenterprise import GetMetricsRequest from .types.recaptchaenterprise import IOSKeySettings from .types.recaptchaenterprise import Key from .types.recaptchaenterprise import ListKeysRequest from .types.recaptchaenterprise import ListKeysResponse +from .types.recaptchaenterprise import Metrics +from .types.recaptchaenterprise import MigrateKeyRequest from .types.recaptchaenterprise import RiskAnalysis +from .types.recaptchaenterprise import ScoreDistribution +from .types.recaptchaenterprise import ScoreMetrics +from .types.recaptchaenterprise import TestingOptions from .types.recaptchaenterprise import TokenProperties from .types.recaptchaenterprise import UpdateKeyRequest from .types.recaptchaenterprise import WebKeySettings @@ -41,17 +48,24 @@ "AnnotateAssessmentRequest", "AnnotateAssessmentResponse", "Assessment", + "ChallengeMetrics", "CreateAssessmentRequest", "CreateKeyRequest", "DeleteKeyRequest", "Event", "GetKeyRequest", + "GetMetricsRequest", "IOSKeySettings", "Key", "ListKeysRequest", "ListKeysResponse", + "Metrics", + "MigrateKeyRequest", "RecaptchaEnterpriseServiceClient", "RiskAnalysis", + "ScoreDistribution", + "ScoreMetrics", + "TestingOptions", "TokenProperties", "UpdateKeyRequest", "WebKeySettings", diff --git a/google/cloud/recaptchaenterprise_v1/gapic_metadata.json b/google/cloud/recaptchaenterprise_v1/gapic_metadata.json index 49c568a..aac10be 100644 --- a/google/cloud/recaptchaenterprise_v1/gapic_metadata.json +++ b/google/cloud/recaptchaenterprise_v1/gapic_metadata.json @@ -35,11 +35,21 @@ "get_key" ] }, + "GetMetrics": { + "methods": [ + "get_metrics" + ] + }, "ListKeys": { "methods": [ "list_keys" ] }, + "MigrateKey": { + "methods": [ + "migrate_key" + ] + }, "UpdateKey": { "methods": [ "update_key" @@ -75,11 +85,21 @@ "get_key" ] }, + "GetMetrics": { + "methods": [ + "get_metrics" + ] + }, "ListKeys": { "methods": [ "list_keys" ] }, + "MigrateKey": { + "methods": [ + "migrate_key" + ] + }, "UpdateKey": { "methods": [ "update_key" diff --git a/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/async_client.py b/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/async_client.py index a2fa070..78ce360 100644 --- a/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/async_client.py +++ b/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/async_client.py @@ -50,6 +50,10 @@ class RecaptchaEnterpriseServiceAsyncClient: ) key_path = staticmethod(RecaptchaEnterpriseServiceClient.key_path) parse_key_path = staticmethod(RecaptchaEnterpriseServiceClient.parse_key_path) + metrics_path = staticmethod(RecaptchaEnterpriseServiceClient.metrics_path) + parse_metrics_path = staticmethod( + RecaptchaEnterpriseServiceClient.parse_metrics_path + ) common_billing_account_path = staticmethod( RecaptchaEnterpriseServiceClient.common_billing_account_path ) @@ -266,7 +270,7 @@ async def annotate_assessment( ) -> recaptchaenterprise.AnnotateAssessmentResponse: r"""Annotates a previously created Assessment to provide additional information on whether the event turned out - to be authentic or fradulent. + to be authentic or fraudulent. Args: request (:class:`google.cloud.recaptchaenterprise_v1.types.AnnotateAssessmentRequest`): @@ -281,8 +285,11 @@ async def annotate_assessment( on the ``request`` instance; if ``request`` is provided, this should not be set. annotation (:class:`google.cloud.recaptchaenterprise_v1.types.AnnotateAssessmentRequest.Annotation`): - Required. The annotation that will be - assigned to the Event. + Optional. The annotation that will be + assigned to the Event. This field can be + left empty to provide reasons that apply + to an event without concluding whether + the event is legitimate or fraudulent. This corresponds to the ``annotation`` field on the ``request`` instance; if ``request`` is provided, this @@ -584,6 +591,131 @@ async def delete_key( request, retry=retry, timeout=timeout, metadata=metadata, ) + async def migrate_key( + self, + request: recaptchaenterprise.MigrateKeyRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> recaptchaenterprise.Key: + r"""Migrates an existing key from reCAPTCHA to reCAPTCHA + Enterprise. Once a key is migrated, it can be used from + either product. SiteVerify requests are billed as + CreateAssessment calls. You must be authenticated as one + of the current owners of the reCAPTCHA Site Key, and + your user must have the reCAPTCHA Enterprise Admin IAM + role in the destination project. + + Args: + request (:class:`google.cloud.recaptchaenterprise_v1.types.MigrateKeyRequest`): + The request object. The migrate key request message. + 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, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.recaptchaenterprise_v1.types.Key: + A key used to identify and configure + applications (web and/or mobile) that + use reCAPTCHA Enterprise. + + """ + # Create or coerce a protobuf request object. + request = recaptchaenterprise.MigrateKeyRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.migrate_key, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # 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),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def get_metrics( + self, + request: recaptchaenterprise.GetMetricsRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> recaptchaenterprise.Metrics: + r"""Get some aggregated metrics for a Key. This data can + be used to build dashboards. + + Args: + request (:class:`google.cloud.recaptchaenterprise_v1.types.GetMetricsRequest`): + The request object. The get metrics request message. + name (:class:`str`): + Required. The name of the requested + metrics, in the format + "projects/{project}/keys/{key}/metrics". + + This corresponds to the ``name`` 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, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.recaptchaenterprise_v1.types.Metrics: + Metrics for a single Key. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + 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." + ) + + request = recaptchaenterprise.GetMetricsRequest(request) + + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method_async.wrap_method( + self._client._transport.get_metrics, + default_timeout=None, + client_info=DEFAULT_CLIENT_INFO, + ) + + # 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),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + try: DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( diff --git a/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/client.py b/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/client.py index 946f2c3..5cc7336 100644 --- a/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/client.py +++ b/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/client.py @@ -186,6 +186,17 @@ def parse_key_path(path: str) -> Dict[str, str]: m = re.match(r"^projects/(?P.+?)/keys/(?P.+?)$", path) return m.groupdict() if m else {} + @staticmethod + def metrics_path(project: str, key: str,) -> str: + """Returns a fully-qualified metrics string.""" + return "projects/{project}/keys/{key}/metrics".format(project=project, key=key,) + + @staticmethod + def parse_metrics_path(path: str) -> Dict[str, str]: + """Parses a metrics path into its component segments.""" + m = re.match(r"^projects/(?P.+?)/keys/(?P.+?)/metrics$", path) + return m.groupdict() if m else {} + @staticmethod def common_billing_account_path(billing_account: str,) -> str: """Returns a fully-qualified billing_account string.""" @@ -456,7 +467,7 @@ def annotate_assessment( ) -> recaptchaenterprise.AnnotateAssessmentResponse: r"""Annotates a previously created Assessment to provide additional information on whether the event turned out - to be authentic or fradulent. + to be authentic or fraudulent. Args: request (google.cloud.recaptchaenterprise_v1.types.AnnotateAssessmentRequest): @@ -471,8 +482,11 @@ def annotate_assessment( on the ``request`` instance; if ``request`` is provided, this should not be set. annotation (google.cloud.recaptchaenterprise_v1.types.AnnotateAssessmentRequest.Annotation): - Required. The annotation that will be - assigned to the Event. + Optional. The annotation that will be + assigned to the Event. This field can be + left empty to provide reasons that apply + to an event without concluding whether + the event is legitimate or fraudulent. This corresponds to the ``annotation`` field on the ``request`` instance; if ``request`` is provided, this @@ -779,6 +793,132 @@ def delete_key( request, retry=retry, timeout=timeout, metadata=metadata, ) + def migrate_key( + self, + request: recaptchaenterprise.MigrateKeyRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> recaptchaenterprise.Key: + r"""Migrates an existing key from reCAPTCHA to reCAPTCHA + Enterprise. Once a key is migrated, it can be used from + either product. SiteVerify requests are billed as + CreateAssessment calls. You must be authenticated as one + of the current owners of the reCAPTCHA Site Key, and + your user must have the reCAPTCHA Enterprise Admin IAM + role in the destination project. + + Args: + request (google.cloud.recaptchaenterprise_v1.types.MigrateKeyRequest): + The request object. The migrate key request message. + 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, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.recaptchaenterprise_v1.types.Key: + A key used to identify and configure + applications (web and/or mobile) that + use reCAPTCHA Enterprise. + + """ + # Create or coerce a protobuf request object. + # Minor optimization to avoid making a copy if the user passes + # in a recaptchaenterprise.MigrateKeyRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, recaptchaenterprise.MigrateKeyRequest): + request = recaptchaenterprise.MigrateKeyRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.migrate_key] + + # 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),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def get_metrics( + self, + request: recaptchaenterprise.GetMetricsRequest = None, + *, + name: str = None, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> recaptchaenterprise.Metrics: + r"""Get some aggregated metrics for a Key. This data can + be used to build dashboards. + + Args: + request (google.cloud.recaptchaenterprise_v1.types.GetMetricsRequest): + The request object. The get metrics request message. + name (str): + Required. The name of the requested + metrics, in the format + "projects/{project}/keys/{key}/metrics". + + This corresponds to the ``name`` 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, str]]): Strings which should be + sent along with the request as metadata. + + Returns: + google.cloud.recaptchaenterprise_v1.types.Metrics: + Metrics for a single Key. + """ + # Create or coerce a protobuf request object. + # Sanity check: If we got a request object, we should *not* have + # gotten any keyword arguments that map to the request. + has_flattened_params = any([name]) + 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." + ) + + # Minor optimization to avoid making a copy if the user passes + # in a recaptchaenterprise.GetMetricsRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, recaptchaenterprise.GetMetricsRequest): + request = recaptchaenterprise.GetMetricsRequest(request) + # If we have keyword arguments corresponding to fields on the + # request, apply these. + if name is not None: + request.name = name + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.get_metrics] + + # 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),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + try: DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( diff --git a/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/base.py b/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/base.py index 69faffa..9e46682 100644 --- a/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/base.py +++ b/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/base.py @@ -178,6 +178,12 @@ def _prep_wrapped_messages(self, client_info): self.delete_key: gapic_v1.method.wrap_method( self.delete_key, default_timeout=600.0, client_info=client_info, ), + self.migrate_key: gapic_v1.method.wrap_method( + self.migrate_key, default_timeout=None, client_info=client_info, + ), + self.get_metrics: gapic_v1.method.wrap_method( + self.get_metrics, default_timeout=None, client_info=client_info, + ), } @property @@ -251,5 +257,23 @@ def delete_key( ]: raise NotImplementedError() + @property + def migrate_key( + self, + ) -> Callable[ + [recaptchaenterprise.MigrateKeyRequest], + Union[recaptchaenterprise.Key, Awaitable[recaptchaenterprise.Key]], + ]: + raise NotImplementedError() + + @property + def get_metrics( + self, + ) -> Callable[ + [recaptchaenterprise.GetMetricsRequest], + Union[recaptchaenterprise.Metrics, Awaitable[recaptchaenterprise.Metrics]], + ]: + raise NotImplementedError() + __all__ = ("RecaptchaEnterpriseServiceTransport",) diff --git a/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/grpc.py b/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/grpc.py index 828dafc..fb5dbf3 100644 --- a/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/grpc.py +++ b/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/grpc.py @@ -266,7 +266,7 @@ def annotate_assessment( Annotates a previously created Assessment to provide additional information on whether the event turned out - to be authentic or fradulent. + to be authentic or fraudulent. Returns: Callable[[~.AnnotateAssessmentRequest], @@ -419,5 +419,64 @@ def delete_key( ) return self._stubs["delete_key"] + @property + def migrate_key( + self, + ) -> Callable[[recaptchaenterprise.MigrateKeyRequest], recaptchaenterprise.Key]: + r"""Return a callable for the migrate key method over gRPC. + + Migrates an existing key from reCAPTCHA to reCAPTCHA + Enterprise. Once a key is migrated, it can be used from + either product. SiteVerify requests are billed as + CreateAssessment calls. You must be authenticated as one + of the current owners of the reCAPTCHA Site Key, and + your user must have the reCAPTCHA Enterprise Admin IAM + role in the destination project. + + Returns: + Callable[[~.MigrateKeyRequest], + ~.Key]: + 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 "migrate_key" not in self._stubs: + self._stubs["migrate_key"] = self.grpc_channel.unary_unary( + "/google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseService/MigrateKey", + request_serializer=recaptchaenterprise.MigrateKeyRequest.serialize, + response_deserializer=recaptchaenterprise.Key.deserialize, + ) + return self._stubs["migrate_key"] + + @property + def get_metrics( + self, + ) -> Callable[[recaptchaenterprise.GetMetricsRequest], recaptchaenterprise.Metrics]: + r"""Return a callable for the get metrics method over gRPC. + + Get some aggregated metrics for a Key. This data can + be used to build dashboards. + + Returns: + Callable[[~.GetMetricsRequest], + ~.Metrics]: + 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 "get_metrics" not in self._stubs: + self._stubs["get_metrics"] = self.grpc_channel.unary_unary( + "/google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseService/GetMetrics", + request_serializer=recaptchaenterprise.GetMetricsRequest.serialize, + response_deserializer=recaptchaenterprise.Metrics.deserialize, + ) + return self._stubs["get_metrics"] + __all__ = ("RecaptchaEnterpriseServiceGrpcTransport",) diff --git a/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/grpc_asyncio.py b/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/grpc_asyncio.py index fd716c3..cd0a764 100644 --- a/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/grpc_asyncio.py +++ b/google/cloud/recaptchaenterprise_v1/services/recaptcha_enterprise_service/transports/grpc_asyncio.py @@ -272,7 +272,7 @@ def annotate_assessment( Annotates a previously created Assessment to provide additional information on whether the event turned out - to be authentic or fradulent. + to be authentic or fraudulent. Returns: Callable[[~.AnnotateAssessmentRequest], @@ -432,5 +432,68 @@ def delete_key( ) return self._stubs["delete_key"] + @property + def migrate_key( + self, + ) -> Callable[ + [recaptchaenterprise.MigrateKeyRequest], Awaitable[recaptchaenterprise.Key] + ]: + r"""Return a callable for the migrate key method over gRPC. + + Migrates an existing key from reCAPTCHA to reCAPTCHA + Enterprise. Once a key is migrated, it can be used from + either product. SiteVerify requests are billed as + CreateAssessment calls. You must be authenticated as one + of the current owners of the reCAPTCHA Site Key, and + your user must have the reCAPTCHA Enterprise Admin IAM + role in the destination project. + + Returns: + Callable[[~.MigrateKeyRequest], + Awaitable[~.Key]]: + 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 "migrate_key" not in self._stubs: + self._stubs["migrate_key"] = self.grpc_channel.unary_unary( + "/google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseService/MigrateKey", + request_serializer=recaptchaenterprise.MigrateKeyRequest.serialize, + response_deserializer=recaptchaenterprise.Key.deserialize, + ) + return self._stubs["migrate_key"] + + @property + def get_metrics( + self, + ) -> Callable[ + [recaptchaenterprise.GetMetricsRequest], Awaitable[recaptchaenterprise.Metrics] + ]: + r"""Return a callable for the get metrics method over gRPC. + + Get some aggregated metrics for a Key. This data can + be used to build dashboards. + + Returns: + Callable[[~.GetMetricsRequest], + Awaitable[~.Metrics]]: + 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 "get_metrics" not in self._stubs: + self._stubs["get_metrics"] = self.grpc_channel.unary_unary( + "/google.cloud.recaptchaenterprise.v1.RecaptchaEnterpriseService/GetMetrics", + request_serializer=recaptchaenterprise.GetMetricsRequest.serialize, + response_deserializer=recaptchaenterprise.Metrics.deserialize, + ) + return self._stubs["get_metrics"] + __all__ = ("RecaptchaEnterpriseServiceGrpcAsyncIOTransport",) diff --git a/google/cloud/recaptchaenterprise_v1/types/__init__.py b/google/cloud/recaptchaenterprise_v1/types/__init__.py index d5e9899..750356b 100644 --- a/google/cloud/recaptchaenterprise_v1/types/__init__.py +++ b/google/cloud/recaptchaenterprise_v1/types/__init__.py @@ -18,16 +18,23 @@ AnnotateAssessmentRequest, AnnotateAssessmentResponse, Assessment, + ChallengeMetrics, CreateAssessmentRequest, CreateKeyRequest, DeleteKeyRequest, Event, GetKeyRequest, + GetMetricsRequest, IOSKeySettings, Key, ListKeysRequest, ListKeysResponse, + Metrics, + MigrateKeyRequest, RiskAnalysis, + ScoreDistribution, + ScoreMetrics, + TestingOptions, TokenProperties, UpdateKeyRequest, WebKeySettings, @@ -38,16 +45,23 @@ "AnnotateAssessmentRequest", "AnnotateAssessmentResponse", "Assessment", + "ChallengeMetrics", "CreateAssessmentRequest", "CreateKeyRequest", "DeleteKeyRequest", "Event", "GetKeyRequest", + "GetMetricsRequest", "IOSKeySettings", "Key", "ListKeysRequest", "ListKeysResponse", + "Metrics", + "MigrateKeyRequest", "RiskAnalysis", + "ScoreDistribution", + "ScoreMetrics", + "TestingOptions", "TokenProperties", "UpdateKeyRequest", "WebKeySettings", diff --git a/google/cloud/recaptchaenterprise_v1/types/recaptchaenterprise.py b/google/cloud/recaptchaenterprise_v1/types/recaptchaenterprise.py index 0869c0c..1aa9908 100644 --- a/google/cloud/recaptchaenterprise_v1/types/recaptchaenterprise.py +++ b/google/cloud/recaptchaenterprise_v1/types/recaptchaenterprise.py @@ -35,10 +35,17 @@ "GetKeyRequest", "UpdateKeyRequest", "DeleteKeyRequest", + "MigrateKeyRequest", + "GetMetricsRequest", + "Metrics", "Key", + "TestingOptions", "WebKeySettings", "AndroidKeySettings", "IOSKeySettings", + "ScoreDistribution", + "ScoreMetrics", + "ChallengeMetrics", }, ) @@ -66,20 +73,40 @@ class AnnotateAssessmentRequest(proto.Message): Assessment, in the format "projects/{project}/assessments/{assessment}". annotation (google.cloud.recaptchaenterprise_v1.types.AnnotateAssessmentRequest.Annotation): - Required. The annotation that will be - assigned to the Event. + Optional. The annotation that will be + assigned to the Event. This field can be left + empty to provide reasons that apply to an event + without concluding whether the event is + legitimate or fraudulent. + reasons (Sequence[google.cloud.recaptchaenterprise_v1.types.AnnotateAssessmentRequest.Reason]): + Optional. Optional reasons for the annotation + that will be assigned to the Event. """ class Annotation(proto.Enum): - r"""Enum that reprensents the types of annotations.""" + r"""Enum that represents the types of annotations.""" ANNOTATION_UNSPECIFIED = 0 LEGITIMATE = 1 FRAUDULENT = 2 PASSWORD_CORRECT = 3 PASSWORD_INCORRECT = 4 + class Reason(proto.Enum): + r"""Enum that represents potential reasons for annotating an + assessment. + """ + REASON_UNSPECIFIED = 0 + CHARGEBACK = 1 + PAYMENT_HEURISTICS = 2 + INITIATED_TWO_FACTOR = 7 + PASSED_TWO_FACTOR = 3 + FAILED_TWO_FACTOR = 4 + CORRECT_PASSWORD = 5 + INCORRECT_PASSWORD = 6 + name = proto.Field(proto.STRING, number=1,) annotation = proto.Field(proto.ENUM, number=2, enum=Annotation,) + reasons = proto.RepeatedField(proto.ENUM, number=3, enum=Reason,) class AnnotateAssessmentResponse(proto.Message): @@ -155,9 +182,7 @@ class RiskAnalysis(proto.Message): """ class ClassificationReason(proto.Enum): - r"""LINT.IfChange(classification_reason) Reasons contributing to the - risk analysis verdict. - """ + r"""Reasons contributing to the risk analysis verdict.""" CLASSIFICATION_REASON_UNSPECIFIED = 0 AUTOMATION = 1 UNEXPECTED_ENVIRONMENT = 2 @@ -193,15 +218,14 @@ class TokenProperties(proto.Message): """ class InvalidReason(proto.Enum): - r"""LINT.IfChange - Enum that represents the types of invalid token reasons. - """ + r"""Enum that represents the types of invalid token reasons.""" INVALID_REASON_UNSPECIFIED = 0 UNKNOWN_INVALID_REASON = 1 MALFORMED = 2 EXPIRED = 3 DUPE = 4 MISSING = 5 + BROWSER_ERROR = 6 valid = proto.Field(proto.BOOL, number=1,) invalid_reason = proto.Field(proto.ENUM, number=2, enum=InvalidReason,) @@ -281,7 +305,7 @@ class UpdateKeyRequest(proto.Message): key (google.cloud.recaptchaenterprise_v1.types.Key): Required. The key to update. update_mask (google.protobuf.field_mask_pb2.FieldMask): - Optional. The mask to control which field of + Optional. The mask to control which fields of the key get updated. If the mask is not present, all fields will be updated. """ @@ -303,6 +327,58 @@ class DeleteKeyRequest(proto.Message): name = proto.Field(proto.STRING, number=1,) +class MigrateKeyRequest(proto.Message): + r"""The migrate key request message. + Attributes: + name (str): + Required. The name of the key to be migrated, + in the format "projects/{project}/keys/{key}". + """ + + name = proto.Field(proto.STRING, number=1,) + + +class GetMetricsRequest(proto.Message): + r"""The get metrics request message. + Attributes: + name (str): + Required. The name of the requested metrics, + in the format + "projects/{project}/keys/{key}/metrics". + """ + + name = proto.Field(proto.STRING, number=1,) + + +class Metrics(proto.Message): + r"""Metrics for a single Key. + Attributes: + name (str): + Output only. The name of the metrics, in the + format "projects/{project}/keys/{key}/metrics". + start_time (google.protobuf.timestamp_pb2.Timestamp): + Inclusive start time aligned to a day (UTC). + score_metrics (Sequence[google.cloud.recaptchaenterprise_v1.types.ScoreMetrics]): + Metrics will be continuous and in order by + dates, and in the granularity of day. All Key + types should have score-based data. + challenge_metrics (Sequence[google.cloud.recaptchaenterprise_v1.types.ChallengeMetrics]): + Metrics will be continuous and in order by + dates, and in the granularity of day. Only + challenge-based keys (CHECKBOX, INVISIBLE), will + have challenge-based data. + """ + + name = proto.Field(proto.STRING, number=4,) + start_time = proto.Field(proto.MESSAGE, number=1, message=timestamp_pb2.Timestamp,) + score_metrics = proto.RepeatedField( + proto.MESSAGE, number=2, message="ScoreMetrics", + ) + challenge_metrics = proto.RepeatedField( + proto.MESSAGE, number=3, message="ChallengeMetrics", + ) + + class Key(proto.Message): r"""A key used to identify and configure applications (web and/or mobile) that use reCAPTCHA Enterprise. @@ -324,13 +400,15 @@ class Key(proto.Message): Settings for keys that can be used by iOS apps. labels (Sequence[google.cloud.recaptchaenterprise_v1.types.Key.LabelsEntry]): - Optional. See Creating and managing labels. create_time (google.protobuf.timestamp_pb2.Timestamp): The timestamp corresponding to the creation of this Key. + testing_options (google.cloud.recaptchaenterprise_v1.types.TestingOptions): + Options for user acceptance testing. """ name = proto.Field(proto.STRING, number=1,) @@ -349,6 +427,33 @@ class Key(proto.Message): ) labels = proto.MapField(proto.STRING, proto.STRING, number=6,) create_time = proto.Field(proto.MESSAGE, number=7, message=timestamp_pb2.Timestamp,) + testing_options = proto.Field(proto.MESSAGE, number=9, message="TestingOptions",) + + +class TestingOptions(proto.Message): + r"""Options for user acceptance testing. + Attributes: + testing_score (float): + All assessments for this Key will return this + score. Must be between 0 (likely not legitimate) + and 1 (likely legitimate) inclusive. + testing_challenge (google.cloud.recaptchaenterprise_v1.types.TestingOptions.TestingChallenge): + For challenge-based keys only (CHECKBOX, + INVISIBLE), all challenge requests for this site + will return nocaptcha if NOCAPTCHA, or an + unsolvable challenge if CHALLENGE. + """ + + class TestingChallenge(proto.Enum): + r"""Enum that represents the challenge option for challenge-based + (CHECKBOX, INVISIBLE) testing keys. + """ + TESTING_CHALLENGE_UNSPECIFIED = 0 + NOCAPTCHA = 1 + UNSOLVABLE_CHALLENGE = 2 + + testing_score = proto.Field(proto.FLOAT, number=1,) + testing_challenge = proto.Field(proto.ENUM, number=2, enum=TestingChallenge,) class WebKeySettings(proto.Message): @@ -366,7 +471,8 @@ class WebKeySettings(proto.Message): or 'subdomain.example.com' allow_amp_traffic (bool): Required. Whether this key can be used on AMP - (Accelerated Mobile Pages) websites. + (Accelerated Mobile Pages) websites. This can + only be set for the SCORE integration type. integration_type (google.cloud.recaptchaenterprise_v1.types.WebKeySettings.IntegrationType): Required. Describes how this key is integrated with the website. @@ -405,24 +511,90 @@ class ChallengeSecurityPreference(proto.Enum): class AndroidKeySettings(proto.Message): r"""Settings specific to keys that can be used by Android apps. Attributes: + allow_all_package_names (bool): + If set to true, it means allowed_package_names will not be + enforced. allowed_package_names (Sequence[str]): Android package names of apps allowed to use the key. Example: 'com.companyname.appname' """ + allow_all_package_names = proto.Field(proto.BOOL, number=2,) allowed_package_names = proto.RepeatedField(proto.STRING, number=1,) class IOSKeySettings(proto.Message): r"""Settings specific to keys that can be used by iOS apps. Attributes: + allow_all_bundle_ids (bool): + If set to true, it means allowed_bundle_ids will not be + enforced. allowed_bundle_ids (Sequence[str]): iOS bundle ids of apps allowed to use the key. Example: 'com.companyname.productname.appname' """ + allow_all_bundle_ids = proto.Field(proto.BOOL, number=2,) allowed_bundle_ids = proto.RepeatedField(proto.STRING, number=1,) +class ScoreDistribution(proto.Message): + r"""Score distribution. + Attributes: + score_buckets (Sequence[google.cloud.recaptchaenterprise_v1.types.ScoreDistribution.ScoreBucketsEntry]): + Map key is score value multiplied by 100. The scores are + discrete values between [0, 1]. The maximum number of + buckets is on order of a few dozen, but typically much lower + (ie. 10). + """ + + score_buckets = proto.MapField(proto.INT32, proto.INT64, number=1,) + + +class ScoreMetrics(proto.Message): + r"""Metrics related to scoring. + Attributes: + overall_metrics (google.cloud.recaptchaenterprise_v1.types.ScoreDistribution): + Aggregated score metrics for all traffic. + action_metrics (Sequence[google.cloud.recaptchaenterprise_v1.types.ScoreMetrics.ActionMetricsEntry]): + Action-based metrics. The map key is the + action name which specified by the site owners + at time of the "execute" client-side call. + Populated only for SCORE keys. + """ + + overall_metrics = proto.Field(proto.MESSAGE, number=1, message="ScoreDistribution",) + action_metrics = proto.MapField( + proto.STRING, proto.MESSAGE, number=2, message="ScoreDistribution", + ) + + +class ChallengeMetrics(proto.Message): + r"""Metrics related to challenges. + Attributes: + pageload_count (int): + Count of reCAPTCHA checkboxes or badges + rendered. This is mostly equivalent to a count + of pageloads for pages that include reCAPTCHA. + nocaptcha_count (int): + Count of nocaptchas (successful verification + without a challenge) issued. + failed_count (int): + Count of submitted challenge solutions that + were incorrect or otherwise deemed suspicious + such that a subsequent challenge was triggered. + passed_count (int): + Count of nocaptchas (successful verification + without a challenge) plus submitted challenge + solutions that were correct and resulted in + verification. + """ + + pageload_count = proto.Field(proto.INT64, number=1,) + nocaptcha_count = proto.Field(proto.INT64, number=2,) + failed_count = proto.Field(proto.INT64, number=3,) + passed_count = proto.Field(proto.INT64, number=4,) + + __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/scripts/fixup_recaptchaenterprise_v1_keywords.py b/scripts/fixup_recaptchaenterprise_v1_keywords.py index 9fe41bb..bde3f26 100644 --- a/scripts/fixup_recaptchaenterprise_v1_keywords.py +++ b/scripts/fixup_recaptchaenterprise_v1_keywords.py @@ -39,12 +39,14 @@ def partition( class recaptchaenterpriseCallTransformer(cst.CSTTransformer): CTRL_PARAMS: Tuple[str] = ('retry', 'timeout', 'metadata') METHOD_TO_PARAMS: Dict[str, Tuple[str]] = { - 'annotate_assessment': ('name', 'annotation', ), + 'annotate_assessment': ('name', 'annotation', 'reasons', ), 'create_assessment': ('parent', 'assessment', ), 'create_key': ('parent', 'key', ), 'delete_key': ('name', ), 'get_key': ('name', ), + 'get_metrics': ('name', ), 'list_keys': ('parent', 'page_size', 'page_token', ), + 'migrate_key': ('name', ), 'update_key': ('key', 'update_mask', ), } diff --git a/tests/unit/gapic/recaptchaenterprise_v1/test_recaptcha_enterprise_service.py b/tests/unit/gapic/recaptchaenterprise_v1/test_recaptcha_enterprise_service.py index c597810..9f1e810 100644 --- a/tests/unit/gapic/recaptchaenterprise_v1/test_recaptcha_enterprise_service.py +++ b/tests/unit/gapic/recaptchaenterprise_v1/test_recaptcha_enterprise_service.py @@ -1857,6 +1857,358 @@ async def test_delete_key_field_headers_async(): assert ("x-goog-request-params", "name=name/value",) in kw["metadata"] +def test_migrate_key( + transport: str = "grpc", request_type=recaptchaenterprise.MigrateKeyRequest +): + client = RecaptchaEnterpriseServiceClient( + 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.migrate_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = recaptchaenterprise.Key( + name="name_value", + display_name="display_name_value", + web_settings=recaptchaenterprise.WebKeySettings(allow_all_domains=True), + ) + response = client.migrate_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == recaptchaenterprise.MigrateKeyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, recaptchaenterprise.Key) + assert response.name == "name_value" + assert response.display_name == "display_name_value" + + +def test_migrate_key_from_dict(): + test_migrate_key(request_type=dict) + + +def test_migrate_key_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 = RecaptchaEnterpriseServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.migrate_key), "__call__") as call: + client.migrate_key() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == recaptchaenterprise.MigrateKeyRequest() + + +@pytest.mark.asyncio +async def test_migrate_key_async( + transport: str = "grpc_asyncio", request_type=recaptchaenterprise.MigrateKeyRequest +): + client = RecaptchaEnterpriseServiceAsyncClient( + 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.migrate_key), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + recaptchaenterprise.Key( + name="name_value", display_name="display_name_value", + ) + ) + response = await client.migrate_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == recaptchaenterprise.MigrateKeyRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, recaptchaenterprise.Key) + assert response.name == "name_value" + assert response.display_name == "display_name_value" + + +@pytest.mark.asyncio +async def test_migrate_key_async_from_dict(): + await test_migrate_key_async(request_type=dict) + + +def test_migrate_key_field_headers(): + client = RecaptchaEnterpriseServiceClient( + 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 = recaptchaenterprise.MigrateKeyRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.migrate_key), "__call__") as call: + call.return_value = recaptchaenterprise.Key() + client.migrate_key(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=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_migrate_key_field_headers_async(): + client = RecaptchaEnterpriseServiceAsyncClient( + 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 = recaptchaenterprise.MigrateKeyRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.migrate_key), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + recaptchaenterprise.Key() + ) + await client.migrate_key(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, 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=name/value",) in kw["metadata"] + + +def test_get_metrics( + transport: str = "grpc", request_type=recaptchaenterprise.GetMetricsRequest +): + client = RecaptchaEnterpriseServiceClient( + 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.get_metrics), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = recaptchaenterprise.Metrics(name="name_value",) + response = client.get_metrics(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == recaptchaenterprise.GetMetricsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, recaptchaenterprise.Metrics) + assert response.name == "name_value" + + +def test_get_metrics_from_dict(): + test_get_metrics(request_type=dict) + + +def test_get_metrics_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 = RecaptchaEnterpriseServiceClient( + credentials=ga_credentials.AnonymousCredentials(), transport="grpc", + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_metrics), "__call__") as call: + client.get_metrics() + call.assert_called() + _, args, _ = call.mock_calls[0] + assert args[0] == recaptchaenterprise.GetMetricsRequest() + + +@pytest.mark.asyncio +async def test_get_metrics_async( + transport: str = "grpc_asyncio", request_type=recaptchaenterprise.GetMetricsRequest +): + client = RecaptchaEnterpriseServiceAsyncClient( + 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.get_metrics), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + recaptchaenterprise.Metrics(name="name_value",) + ) + response = await client.get_metrics(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0] == recaptchaenterprise.GetMetricsRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, recaptchaenterprise.Metrics) + assert response.name == "name_value" + + +@pytest.mark.asyncio +async def test_get_metrics_async_from_dict(): + await test_get_metrics_async(request_type=dict) + + +def test_get_metrics_field_headers(): + client = RecaptchaEnterpriseServiceClient( + 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 = recaptchaenterprise.GetMetricsRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_metrics), "__call__") as call: + call.return_value = recaptchaenterprise.Metrics() + client.get_metrics(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=name/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_metrics_field_headers_async(): + client = RecaptchaEnterpriseServiceAsyncClient( + 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 = recaptchaenterprise.GetMetricsRequest() + + request.name = "name/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_metrics), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + recaptchaenterprise.Metrics() + ) + await client.get_metrics(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, 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=name/value",) in kw["metadata"] + + +def test_get_metrics_flattened(): + client = RecaptchaEnterpriseServiceClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_metrics), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = recaptchaenterprise.Metrics() + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + client.get_metrics(name="name_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] + assert args[0].name == "name_value" + + +def test_get_metrics_flattened_error(): + client = RecaptchaEnterpriseServiceClient( + 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.get_metrics( + recaptchaenterprise.GetMetricsRequest(), name="name_value", + ) + + +@pytest.mark.asyncio +async def test_get_metrics_flattened_async(): + client = RecaptchaEnterpriseServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_metrics), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = recaptchaenterprise.Metrics() + + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + recaptchaenterprise.Metrics() + ) + # Call the method with a truthy value for each flattened field, + # using the keyword arguments to the method. + response = await client.get_metrics(name="name_value",) + + # Establish that the underlying call was made with the expected + # request object values. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + assert args[0].name == "name_value" + + +@pytest.mark.asyncio +async def test_get_metrics_flattened_error_async(): + client = RecaptchaEnterpriseServiceAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Attempting to call a method with both a request object and flattened + # fields is an error. + with pytest.raises(ValueError): + await client.get_metrics( + recaptchaenterprise.GetMetricsRequest(), name="name_value", + ) + + def test_credentials_transport_error(): # It is an error to provide credentials and a transport instance. transport = transports.RecaptchaEnterpriseServiceGrpcTransport( @@ -1965,6 +2317,8 @@ def test_recaptcha_enterprise_service_base_transport(): "get_key", "update_key", "delete_key", + "migrate_key", + "get_metrics", ) for method in methods: with pytest.raises(NotImplementedError): @@ -2361,8 +2715,28 @@ def test_parse_key_path(): assert expected == actual +def test_metrics_path(): + project = "winkle" + key = "nautilus" + expected = "projects/{project}/keys/{key}/metrics".format(project=project, key=key,) + actual = RecaptchaEnterpriseServiceClient.metrics_path(project, key) + assert expected == actual + + +def test_parse_metrics_path(): + expected = { + "project": "scallop", + "key": "abalone", + } + path = RecaptchaEnterpriseServiceClient.metrics_path(**expected) + + # Check that the path construction is reversible. + actual = RecaptchaEnterpriseServiceClient.parse_metrics_path(path) + assert expected == actual + + def test_common_billing_account_path(): - billing_account = "winkle" + billing_account = "squid" expected = "billingAccounts/{billing_account}".format( billing_account=billing_account, ) @@ -2374,7 +2748,7 @@ def test_common_billing_account_path(): def test_parse_common_billing_account_path(): expected = { - "billing_account": "nautilus", + "billing_account": "clam", } path = RecaptchaEnterpriseServiceClient.common_billing_account_path(**expected) @@ -2384,7 +2758,7 @@ def test_parse_common_billing_account_path(): def test_common_folder_path(): - folder = "scallop" + folder = "whelk" expected = "folders/{folder}".format(folder=folder,) actual = RecaptchaEnterpriseServiceClient.common_folder_path(folder) assert expected == actual @@ -2392,7 +2766,7 @@ def test_common_folder_path(): def test_parse_common_folder_path(): expected = { - "folder": "abalone", + "folder": "octopus", } path = RecaptchaEnterpriseServiceClient.common_folder_path(**expected) @@ -2402,7 +2776,7 @@ def test_parse_common_folder_path(): def test_common_organization_path(): - organization = "squid" + organization = "oyster" expected = "organizations/{organization}".format(organization=organization,) actual = RecaptchaEnterpriseServiceClient.common_organization_path(organization) assert expected == actual @@ -2410,7 +2784,7 @@ def test_common_organization_path(): def test_parse_common_organization_path(): expected = { - "organization": "clam", + "organization": "nudibranch", } path = RecaptchaEnterpriseServiceClient.common_organization_path(**expected) @@ -2420,7 +2794,7 @@ def test_parse_common_organization_path(): def test_common_project_path(): - project = "whelk" + project = "cuttlefish" expected = "projects/{project}".format(project=project,) actual = RecaptchaEnterpriseServiceClient.common_project_path(project) assert expected == actual @@ -2428,7 +2802,7 @@ def test_common_project_path(): def test_parse_common_project_path(): expected = { - "project": "octopus", + "project": "mussel", } path = RecaptchaEnterpriseServiceClient.common_project_path(**expected) @@ -2438,8 +2812,8 @@ def test_parse_common_project_path(): def test_common_location_path(): - project = "oyster" - location = "nudibranch" + project = "winkle" + location = "nautilus" expected = "projects/{project}/locations/{location}".format( project=project, location=location, ) @@ -2449,8 +2823,8 @@ def test_common_location_path(): def test_parse_common_location_path(): expected = { - "project": "cuttlefish", - "location": "mussel", + "project": "scallop", + "location": "abalone", } path = RecaptchaEnterpriseServiceClient.common_location_path(**expected)