diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh index 9c43c66..7653933 100755 --- a/.kokoro/test-samples.sh +++ b/.kokoro/test-samples.sh @@ -87,11 +87,11 @@ for file in samples/**/requirements.txt; do python3.6 -m nox -s "$RUN_TESTS_SESSION" EXIT=$? - # If this is a periodic build, send the test log to the FlakyBot. - # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/flakybot. + # If this is a periodic build, send the test log to the Build Cop Bot. + # See https://github.com/googleapis/repo-automation-bots/tree/master/packages/buildcop. if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then - chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot - $KOKORO_GFILE_DIR/linux_amd64/flakybot + chmod +x $KOKORO_GFILE_DIR/linux_amd64/buildcop + $KOKORO_GFILE_DIR/linux_amd64/buildcop fi if [[ $EXIT -ne 0 ]]; then diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index 4af6cdc..719bcd5 100755 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -159,7 +159,7 @@ if [[ -n "${KOKORO_BUILD_ID:-}" ]]; then "KOKORO_GITHUB_COMMIT" "KOKORO_GITHUB_PULL_REQUEST_NUMBER" "KOKORO_GITHUB_PULL_REQUEST_COMMIT" - # For FlakyBot + # For Build Cop Bot "KOKORO_GITHUB_COMMIT_URL" "KOKORO_GITHUB_PULL_REQUEST_URL" ) diff --git a/google/cloud/channel/__init__.py b/google/cloud/channel/__init__.py index 36fa37f..8cf0772 100644 --- a/google/cloud/channel/__init__.py +++ b/google/cloud/channel/__init__.py @@ -93,6 +93,8 @@ from google.cloud.channel_v1.types.service import ListPurchasableSkusResponse from google.cloud.channel_v1.types.service import ListSkusRequest from google.cloud.channel_v1.types.service import ListSkusResponse +from google.cloud.channel_v1.types.service import ListSubscribersRequest +from google.cloud.channel_v1.types.service import ListSubscribersResponse from google.cloud.channel_v1.types.service import ListTransferableOffersRequest from google.cloud.channel_v1.types.service import ListTransferableOffersResponse from google.cloud.channel_v1.types.service import ListTransferableSkusRequest @@ -100,14 +102,21 @@ from google.cloud.channel_v1.types.service import ProvisionCloudIdentityRequest from google.cloud.channel_v1.types.service import PurchasableOffer from google.cloud.channel_v1.types.service import PurchasableSku +from google.cloud.channel_v1.types.service import RegisterSubscriberRequest +from google.cloud.channel_v1.types.service import RegisterSubscriberResponse from google.cloud.channel_v1.types.service import StartPaidServiceRequest from google.cloud.channel_v1.types.service import SuspendEntitlementRequest from google.cloud.channel_v1.types.service import TransferEntitlementsRequest from google.cloud.channel_v1.types.service import TransferEntitlementsResponse from google.cloud.channel_v1.types.service import TransferEntitlementsToGoogleRequest from google.cloud.channel_v1.types.service import TransferableOffer +from google.cloud.channel_v1.types.service import UnregisterSubscriberRequest +from google.cloud.channel_v1.types.service import UnregisterSubscriberResponse from google.cloud.channel_v1.types.service import UpdateChannelPartnerLinkRequest from google.cloud.channel_v1.types.service import UpdateCustomerRequest +from google.cloud.channel_v1.types.subscriber_event import CustomerEvent +from google.cloud.channel_v1.types.subscriber_event import EntitlementEvent +from google.cloud.channel_v1.types.subscriber_event import SubscriberEvent __all__ = ( "ActivateEntitlementRequest", @@ -134,9 +143,11 @@ "CreateEntitlementRequest", "Customer", "CustomerConstraints", + "CustomerEvent", "DeleteCustomerRequest", "EduData", "Entitlement", + "EntitlementEvent", "GetChannelPartnerLinkRequest", "GetCustomerRequest", "GetEntitlementRequest", @@ -156,6 +167,8 @@ "ListPurchasableSkusResponse", "ListSkusRequest", "ListSkusResponse", + "ListSubscribersRequest", + "ListSubscribersResponse", "ListTransferableOffersRequest", "ListTransferableOffersResponse", "ListTransferableSkusRequest", @@ -182,10 +195,13 @@ "ProvisionedService", "PurchasableOffer", "PurchasableSku", + "RegisterSubscriberRequest", + "RegisterSubscriberResponse", "RenewalSettings", "ResourceType", "Sku", "StartPaidServiceRequest", + "SubscriberEvent", "SuspendEntitlementRequest", "TransferEligibility", "TransferEntitlementsRequest", @@ -194,6 +210,8 @@ "TransferableOffer", "TransferableSku", "TrialSettings", + "UnregisterSubscriberRequest", + "UnregisterSubscriberResponse", "UpdateChannelPartnerLinkRequest", "UpdateCustomerRequest", "Value", diff --git a/google/cloud/channel_v1/__init__.py b/google/cloud/channel_v1/__init__.py index bd1a55a..19d3127 100644 --- a/google/cloud/channel_v1/__init__.py +++ b/google/cloud/channel_v1/__init__.py @@ -86,6 +86,8 @@ from .types.service import ListPurchasableSkusResponse from .types.service import ListSkusRequest from .types.service import ListSkusResponse +from .types.service import ListSubscribersRequest +from .types.service import ListSubscribersResponse from .types.service import ListTransferableOffersRequest from .types.service import ListTransferableOffersResponse from .types.service import ListTransferableSkusRequest @@ -93,14 +95,21 @@ from .types.service import ProvisionCloudIdentityRequest from .types.service import PurchasableOffer from .types.service import PurchasableSku +from .types.service import RegisterSubscriberRequest +from .types.service import RegisterSubscriberResponse from .types.service import StartPaidServiceRequest from .types.service import SuspendEntitlementRequest from .types.service import TransferEntitlementsRequest from .types.service import TransferEntitlementsResponse from .types.service import TransferEntitlementsToGoogleRequest from .types.service import TransferableOffer +from .types.service import UnregisterSubscriberRequest +from .types.service import UnregisterSubscriberResponse from .types.service import UpdateChannelPartnerLinkRequest from .types.service import UpdateCustomerRequest +from .types.subscriber_event import CustomerEvent +from .types.subscriber_event import EntitlementEvent +from .types.subscriber_event import SubscriberEvent __all__ = ( @@ -126,9 +135,11 @@ "CreateEntitlementRequest", "Customer", "CustomerConstraints", + "CustomerEvent", "DeleteCustomerRequest", "EduData", "Entitlement", + "EntitlementEvent", "GetChannelPartnerLinkRequest", "GetCustomerRequest", "GetEntitlementRequest", @@ -148,6 +159,8 @@ "ListPurchasableSkusResponse", "ListSkusRequest", "ListSkusResponse", + "ListSubscribersRequest", + "ListSubscribersResponse", "ListTransferableOffersRequest", "ListTransferableOffersResponse", "ListTransferableSkusRequest", @@ -174,10 +187,13 @@ "ProvisionedService", "PurchasableOffer", "PurchasableSku", + "RegisterSubscriberRequest", + "RegisterSubscriberResponse", "RenewalSettings", "ResourceType", "Sku", "StartPaidServiceRequest", + "SubscriberEvent", "SuspendEntitlementRequest", "TransferEligibility", "TransferEntitlementsRequest", @@ -186,6 +202,8 @@ "TransferableOffer", "TransferableSku", "TrialSettings", + "UnregisterSubscriberRequest", + "UnregisterSubscriberResponse", "UpdateChannelPartnerLinkRequest", "UpdateCustomerRequest", "Value", diff --git a/google/cloud/channel_v1/services/cloud_channel_service/async_client.py b/google/cloud/channel_v1/services/cloud_channel_service/async_client.py index 3913173..cd80746 100644 --- a/google/cloud/channel_v1/services/cloud_channel_service/async_client.py +++ b/google/cloud/channel_v1/services/cloud_channel_service/async_client.py @@ -2608,6 +2608,223 @@ async def list_purchasable_offers( # Done; return the response. return response + async def register_subscriber( + self, + request: service.RegisterSubscriberRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> service.RegisterSubscriberResponse: + r"""Registers a service account with subscriber privileges on the + Cloud Pub/Sub topic created for this Channel Services account. + Once you create a subscriber, you will get the events as per + [SubscriberEvent][google.cloud.channel.v1.SubscriberEvent] + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the impersonated user is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: Topic name with service email address registered + if successful, otherwise error is returned. + + Args: + request (:class:`google.cloud.channel_v1.types.RegisterSubscriberRequest`): + The request object. Request Message for + RegisterSubscriber. + + 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.channel_v1.types.RegisterSubscriberResponse: + Response Message for + RegisterSubscriber. + + """ + # Create or coerce a protobuf request object. + + request = service.RegisterSubscriberRequest(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.register_subscriber, + 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((("account", request.account),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def unregister_subscriber( + self, + request: service.UnregisterSubscriberRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> service.UnregisterSubscriberResponse: + r"""Unregisters a service account with subscriber privileges on the + Cloud Pub/Sub topic created for this Channel Services account. + If there are no more service account left with sunbscriber + privileges, the topic will be deleted. You can check this by + calling ListSubscribers api. + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the impersonated user is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - NOT_FOUND: If the topic resource doesn't exist. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: Topic name from which service email address has + been unregistered if successful, otherwise error is returned. If + the service email was already not associated with the topic, the + success response will be returned. + + Args: + request (:class:`google.cloud.channel_v1.types.UnregisterSubscriberRequest`): + The request object. Request Message for + UnregisterSubscriber. + + 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.channel_v1.types.UnregisterSubscriberResponse: + Response Message for + UnregisterSubscriber. + + """ + # Create or coerce a protobuf request object. + + request = service.UnregisterSubscriberRequest(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.unregister_subscriber, + 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((("account", request.account),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + async def list_subscribers( + self, + request: service.ListSubscribersRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListSubscribersAsyncPager: + r"""Lists service accounts with subscriber privileges on the Cloud + Pub/Sub topic created for this Channel Services account. + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the account is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - NOT_FOUND: If the topic resource doesn't exist. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: List of service email addresses if successful, + otherwise error is returned. + + Args: + request (:class:`google.cloud.channel_v1.types.ListSubscribersRequest`): + The request object. Request Message for ListSubscribers. + + 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.channel_v1.services.cloud_channel_service.pagers.ListSubscribersAsyncPager: + Response Message for ListSubscribers. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + + request = service.ListSubscribersRequest(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.list_subscribers, + 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((("account", request.account),)), + ) + + # Send the request. + response = await rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__aiter__` convenience method. + response = pagers.ListSubscribersAsyncPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + try: DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( diff --git a/google/cloud/channel_v1/services/cloud_channel_service/client.py b/google/cloud/channel_v1/services/cloud_channel_service/client.py index 864eeee..0bb610f 100644 --- a/google/cloud/channel_v1/services/cloud_channel_service/client.py +++ b/google/cloud/channel_v1/services/cloud_channel_service/client.py @@ -374,21 +374,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: @@ -431,7 +427,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, ) @@ -2891,6 +2887,226 @@ def list_purchasable_offers( # Done; return the response. return response + def register_subscriber( + self, + request: service.RegisterSubscriberRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> service.RegisterSubscriberResponse: + r"""Registers a service account with subscriber privileges on the + Cloud Pub/Sub topic created for this Channel Services account. + Once you create a subscriber, you will get the events as per + [SubscriberEvent][google.cloud.channel.v1.SubscriberEvent] + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the impersonated user is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: Topic name with service email address registered + if successful, otherwise error is returned. + + Args: + request (google.cloud.channel_v1.types.RegisterSubscriberRequest): + The request object. Request Message for + RegisterSubscriber. + + 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.channel_v1.types.RegisterSubscriberResponse: + Response Message for + RegisterSubscriber. + + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a service.RegisterSubscriberRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, service.RegisterSubscriberRequest): + request = service.RegisterSubscriberRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.register_subscriber] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("account", request.account),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def unregister_subscriber( + self, + request: service.UnregisterSubscriberRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> service.UnregisterSubscriberResponse: + r"""Unregisters a service account with subscriber privileges on the + Cloud Pub/Sub topic created for this Channel Services account. + If there are no more service account left with sunbscriber + privileges, the topic will be deleted. You can check this by + calling ListSubscribers api. + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the impersonated user is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - NOT_FOUND: If the topic resource doesn't exist. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: Topic name from which service email address has + been unregistered if successful, otherwise error is returned. If + the service email was already not associated with the topic, the + success response will be returned. + + Args: + request (google.cloud.channel_v1.types.UnregisterSubscriberRequest): + The request object. Request Message for + UnregisterSubscriber. + + 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.channel_v1.types.UnregisterSubscriberResponse: + Response Message for + UnregisterSubscriber. + + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a service.UnregisterSubscriberRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, service.UnregisterSubscriberRequest): + request = service.UnregisterSubscriberRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.unregister_subscriber] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("account", request.account),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # Done; return the response. + return response + + def list_subscribers( + self, + request: service.ListSubscribersRequest = None, + *, + retry: retries.Retry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> pagers.ListSubscribersPager: + r"""Lists service accounts with subscriber privileges on the Cloud + Pub/Sub topic created for this Channel Services account. + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the account is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - NOT_FOUND: If the topic resource doesn't exist. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: List of service email addresses if successful, + otherwise error is returned. + + Args: + request (google.cloud.channel_v1.types.ListSubscribersRequest): + The request object. Request Message for ListSubscribers. + + 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.channel_v1.services.cloud_channel_service.pagers.ListSubscribersPager: + Response Message for ListSubscribers. + Iterating over this object will yield + results and resolve additional pages + automatically. + + """ + # Create or coerce a protobuf request object. + + # Minor optimization to avoid making a copy if the user passes + # in a service.ListSubscribersRequest. + # There's no risk of modifying the input as we've already verified + # there are no flattened fields. + if not isinstance(request, service.ListSubscribersRequest): + request = service.ListSubscribersRequest(request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = self._transport._wrapped_methods[self._transport.list_subscribers] + + # Certain fields should be provided within the metadata header; + # add these here. + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("account", request.account),)), + ) + + # Send the request. + response = rpc(request, retry=retry, timeout=timeout, metadata=metadata,) + + # This method is paged; wrap the response in a pager, which provides + # an `__iter__` convenience method. + response = pagers.ListSubscribersPager( + method=rpc, request=request, response=response, metadata=metadata, + ) + + # Done; return the response. + return response + try: DEFAULT_CLIENT_INFO = gapic_v1.client_info.ClientInfo( diff --git a/google/cloud/channel_v1/services/cloud_channel_service/pagers.py b/google/cloud/channel_v1/services/cloud_channel_service/pagers.py index 20a1634..10516b4 100644 --- a/google/cloud/channel_v1/services/cloud_channel_service/pagers.py +++ b/google/cloud/channel_v1/services/cloud_channel_service/pagers.py @@ -1303,3 +1303,131 @@ async def async_generator(): def __repr__(self) -> str: return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListSubscribersPager: + """A pager for iterating through ``list_subscribers`` requests. + + This class thinly wraps an initial + :class:`google.cloud.channel_v1.types.ListSubscribersResponse` object, and + provides an ``__iter__`` method to iterate through its + ``service_accounts`` field. + + If there are more pages, the ``__iter__`` method will make additional + ``ListSubscribers`` requests and continue to iterate + through the ``service_accounts`` field on the + corresponding responses. + + All the usual :class:`google.cloud.channel_v1.types.ListSubscribersResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., service.ListSubscribersResponse], + request: service.ListSubscribersRequest, + response: service.ListSubscribersResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.channel_v1.types.ListSubscribersRequest): + The initial request object. + response (google.cloud.channel_v1.types.ListSubscribersResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = service.ListSubscribersRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + def pages(self) -> Iterable[service.ListSubscribersResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = self._method(self._request, metadata=self._metadata) + yield self._response + + def __iter__(self) -> Iterable[str]: + for page in self.pages: + yield from page.service_accounts + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) + + +class ListSubscribersAsyncPager: + """A pager for iterating through ``list_subscribers`` requests. + + This class thinly wraps an initial + :class:`google.cloud.channel_v1.types.ListSubscribersResponse` object, and + provides an ``__aiter__`` method to iterate through its + ``service_accounts`` field. + + If there are more pages, the ``__aiter__`` method will make additional + ``ListSubscribers`` requests and continue to iterate + through the ``service_accounts`` field on the + corresponding responses. + + All the usual :class:`google.cloud.channel_v1.types.ListSubscribersResponse` + attributes are available on the pager. If multiple requests are made, only + the most recent response is retained, and thus used for attribute lookup. + """ + + def __init__( + self, + method: Callable[..., Awaitable[service.ListSubscribersResponse]], + request: service.ListSubscribersRequest, + response: service.ListSubscribersResponse, + *, + metadata: Sequence[Tuple[str, str]] = () + ): + """Instantiate the pager. + + Args: + method (Callable): The method that was originally called, and + which instantiated this pager. + request (google.cloud.channel_v1.types.ListSubscribersRequest): + The initial request object. + response (google.cloud.channel_v1.types.ListSubscribersResponse): + The initial response object. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + """ + self._method = method + self._request = service.ListSubscribersRequest(request) + self._response = response + self._metadata = metadata + + def __getattr__(self, name: str) -> Any: + return getattr(self._response, name) + + @property + async def pages(self) -> AsyncIterable[service.ListSubscribersResponse]: + yield self._response + while self._response.next_page_token: + self._request.page_token = self._response.next_page_token + self._response = await self._method(self._request, metadata=self._metadata) + yield self._response + + def __aiter__(self) -> AsyncIterable[str]: + async def async_generator(): + async for page in self.pages: + for response in page.service_accounts: + yield response + + return async_generator() + + def __repr__(self) -> str: + return "{0}<{1!r}>".format(self.__class__.__name__, self._response) diff --git a/google/cloud/channel_v1/services/cloud_channel_service/transports/base.py b/google/cloud/channel_v1/services/cloud_channel_service/transports/base.py index 909c799..638837c 100644 --- a/google/cloud/channel_v1/services/cloud_channel_service/transports/base.py +++ b/google/cloud/channel_v1/services/cloud_channel_service/transports/base.py @@ -228,6 +228,17 @@ def _prep_wrapped_messages(self, client_info): default_timeout=None, client_info=client_info, ), + self.register_subscriber: gapic_v1.method.wrap_method( + self.register_subscriber, default_timeout=None, client_info=client_info, + ), + self.unregister_subscriber: gapic_v1.method.wrap_method( + self.unregister_subscriber, + default_timeout=None, + client_info=client_info, + ), + self.list_subscribers: gapic_v1.method.wrap_method( + self.list_subscribers, default_timeout=None, client_info=client_info, + ), } @property @@ -546,5 +557,41 @@ def list_purchasable_offers( ]: raise NotImplementedError() + @property + def register_subscriber( + self, + ) -> typing.Callable[ + [service.RegisterSubscriberRequest], + typing.Union[ + service.RegisterSubscriberResponse, + typing.Awaitable[service.RegisterSubscriberResponse], + ], + ]: + raise NotImplementedError() + + @property + def unregister_subscriber( + self, + ) -> typing.Callable[ + [service.UnregisterSubscriberRequest], + typing.Union[ + service.UnregisterSubscriberResponse, + typing.Awaitable[service.UnregisterSubscriberResponse], + ], + ]: + raise NotImplementedError() + + @property + def list_subscribers( + self, + ) -> typing.Callable[ + [service.ListSubscribersRequest], + typing.Union[ + service.ListSubscribersResponse, + typing.Awaitable[service.ListSubscribersResponse], + ], + ]: + raise NotImplementedError() + __all__ = ("CloudChannelServiceTransport",) diff --git a/google/cloud/channel_v1/services/cloud_channel_service/transports/grpc.py b/google/cloud/channel_v1/services/cloud_channel_service/transports/grpc.py index dcddb4a..7ef0a39 100644 --- a/google/cloud/channel_v1/services/cloud_channel_service/transports/grpc.py +++ b/google/cloud/channel_v1/services/cloud_channel_service/transports/grpc.py @@ -91,6 +91,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: @@ -121,6 +122,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): @@ -137,6 +142,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. @@ -146,11 +156,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 @@ -194,12 +199,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=[ @@ -1708,5 +1719,144 @@ def list_purchasable_offers( ) return self._stubs["list_purchasable_offers"] + @property + def register_subscriber( + self, + ) -> Callable[ + [service.RegisterSubscriberRequest], service.RegisterSubscriberResponse + ]: + r"""Return a callable for the register subscriber method over gRPC. + + Registers a service account with subscriber privileges on the + Cloud Pub/Sub topic created for this Channel Services account. + Once you create a subscriber, you will get the events as per + [SubscriberEvent][google.cloud.channel.v1.SubscriberEvent] + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the impersonated user is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: Topic name with service email address registered + if successful, otherwise error is returned. + + Returns: + Callable[[~.RegisterSubscriberRequest], + ~.RegisterSubscriberResponse]: + 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 "register_subscriber" not in self._stubs: + self._stubs["register_subscriber"] = self.grpc_channel.unary_unary( + "/google.cloud.channel.v1.CloudChannelService/RegisterSubscriber", + request_serializer=service.RegisterSubscriberRequest.serialize, + response_deserializer=service.RegisterSubscriberResponse.deserialize, + ) + return self._stubs["register_subscriber"] + + @property + def unregister_subscriber( + self, + ) -> Callable[ + [service.UnregisterSubscriberRequest], service.UnregisterSubscriberResponse + ]: + r"""Return a callable for the unregister subscriber method over gRPC. + + Unregisters a service account with subscriber privileges on the + Cloud Pub/Sub topic created for this Channel Services account. + If there are no more service account left with sunbscriber + privileges, the topic will be deleted. You can check this by + calling ListSubscribers api. + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the impersonated user is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - NOT_FOUND: If the topic resource doesn't exist. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: Topic name from which service email address has + been unregistered if successful, otherwise error is returned. If + the service email was already not associated with the topic, the + success response will be returned. + + Returns: + Callable[[~.UnregisterSubscriberRequest], + ~.UnregisterSubscriberResponse]: + 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 "unregister_subscriber" not in self._stubs: + self._stubs["unregister_subscriber"] = self.grpc_channel.unary_unary( + "/google.cloud.channel.v1.CloudChannelService/UnregisterSubscriber", + request_serializer=service.UnregisterSubscriberRequest.serialize, + response_deserializer=service.UnregisterSubscriberResponse.deserialize, + ) + return self._stubs["unregister_subscriber"] + + @property + def list_subscribers( + self, + ) -> Callable[[service.ListSubscribersRequest], service.ListSubscribersResponse]: + r"""Return a callable for the list subscribers method over gRPC. + + Lists service accounts with subscriber privileges on the Cloud + Pub/Sub topic created for this Channel Services account. + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the account is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - NOT_FOUND: If the topic resource doesn't exist. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: List of service email addresses if successful, + otherwise error is returned. + + Returns: + Callable[[~.ListSubscribersRequest], + ~.ListSubscribersResponse]: + 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 "list_subscribers" not in self._stubs: + self._stubs["list_subscribers"] = self.grpc_channel.unary_unary( + "/google.cloud.channel.v1.CloudChannelService/ListSubscribers", + request_serializer=service.ListSubscribersRequest.serialize, + response_deserializer=service.ListSubscribersResponse.deserialize, + ) + return self._stubs["list_subscribers"] + __all__ = ("CloudChannelServiceGrpcTransport",) diff --git a/google/cloud/channel_v1/services/cloud_channel_service/transports/grpc_asyncio.py b/google/cloud/channel_v1/services/cloud_channel_service/transports/grpc_asyncio.py index 826babb..51b6913 100644 --- a/google/cloud/channel_v1/services/cloud_channel_service/transports/grpc_asyncio.py +++ b/google/cloud/channel_v1/services/cloud_channel_service/transports/grpc_asyncio.py @@ -135,6 +135,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: @@ -166,6 +167,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): @@ -182,6 +187,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. @@ -191,11 +201,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 @@ -239,12 +244,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=[ @@ -1737,5 +1748,148 @@ def list_purchasable_offers( ) return self._stubs["list_purchasable_offers"] + @property + def register_subscriber( + self, + ) -> Callable[ + [service.RegisterSubscriberRequest], + Awaitable[service.RegisterSubscriberResponse], + ]: + r"""Return a callable for the register subscriber method over gRPC. + + Registers a service account with subscriber privileges on the + Cloud Pub/Sub topic created for this Channel Services account. + Once you create a subscriber, you will get the events as per + [SubscriberEvent][google.cloud.channel.v1.SubscriberEvent] + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the impersonated user is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: Topic name with service email address registered + if successful, otherwise error is returned. + + Returns: + Callable[[~.RegisterSubscriberRequest], + Awaitable[~.RegisterSubscriberResponse]]: + 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 "register_subscriber" not in self._stubs: + self._stubs["register_subscriber"] = self.grpc_channel.unary_unary( + "/google.cloud.channel.v1.CloudChannelService/RegisterSubscriber", + request_serializer=service.RegisterSubscriberRequest.serialize, + response_deserializer=service.RegisterSubscriberResponse.deserialize, + ) + return self._stubs["register_subscriber"] + + @property + def unregister_subscriber( + self, + ) -> Callable[ + [service.UnregisterSubscriberRequest], + Awaitable[service.UnregisterSubscriberResponse], + ]: + r"""Return a callable for the unregister subscriber method over gRPC. + + Unregisters a service account with subscriber privileges on the + Cloud Pub/Sub topic created for this Channel Services account. + If there are no more service account left with sunbscriber + privileges, the topic will be deleted. You can check this by + calling ListSubscribers api. + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the impersonated user is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - NOT_FOUND: If the topic resource doesn't exist. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: Topic name from which service email address has + been unregistered if successful, otherwise error is returned. If + the service email was already not associated with the topic, the + success response will be returned. + + Returns: + Callable[[~.UnregisterSubscriberRequest], + Awaitable[~.UnregisterSubscriberResponse]]: + 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 "unregister_subscriber" not in self._stubs: + self._stubs["unregister_subscriber"] = self.grpc_channel.unary_unary( + "/google.cloud.channel.v1.CloudChannelService/UnregisterSubscriber", + request_serializer=service.UnregisterSubscriberRequest.serialize, + response_deserializer=service.UnregisterSubscriberResponse.deserialize, + ) + return self._stubs["unregister_subscriber"] + + @property + def list_subscribers( + self, + ) -> Callable[ + [service.ListSubscribersRequest], Awaitable[service.ListSubscribersResponse] + ]: + r"""Return a callable for the list subscribers method over gRPC. + + Lists service accounts with subscriber privileges on the Cloud + Pub/Sub topic created for this Channel Services account. + + Possible Error Codes: + + - PERMISSION_DENIED: If the reseller account making the request + and the reseller account being provided are different, or if + the account is not a super admin. + - INVALID_ARGUMENT: Missing or invalid required parameters in + the request. + - NOT_FOUND: If the topic resource doesn't exist. + - INTERNAL: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + - UNKNOWN: Any non-user error related to a technical issue in + the backend. In this case, contact Cloud Channel support. + + Return Value: List of service email addresses if successful, + otherwise error is returned. + + Returns: + Callable[[~.ListSubscribersRequest], + Awaitable[~.ListSubscribersResponse]]: + 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 "list_subscribers" not in self._stubs: + self._stubs["list_subscribers"] = self.grpc_channel.unary_unary( + "/google.cloud.channel.v1.CloudChannelService/ListSubscribers", + request_serializer=service.ListSubscribersRequest.serialize, + response_deserializer=service.ListSubscribersResponse.deserialize, + ) + return self._stubs["list_subscribers"] + __all__ = ("CloudChannelServiceGrpcAsyncIOTransport",) diff --git a/google/cloud/channel_v1/types/__init__.py b/google/cloud/channel_v1/types/__init__.py index 69b85e2..3acdc3a 100644 --- a/google/cloud/channel_v1/types/__init__.py +++ b/google/cloud/channel_v1/types/__init__.py @@ -113,6 +113,17 @@ ListPurchasableOffersRequest, ListPurchasableOffersResponse, PurchasableOffer, + RegisterSubscriberRequest, + RegisterSubscriberResponse, + UnregisterSubscriberRequest, + UnregisterSubscriberResponse, + ListSubscribersRequest, + ListSubscribersResponse, +) +from .subscriber_event import ( + CustomerEvent, + EntitlementEvent, + SubscriberEvent, ) __all__ = ( @@ -201,4 +212,13 @@ "ListPurchasableOffersRequest", "ListPurchasableOffersResponse", "PurchasableOffer", + "RegisterSubscriberRequest", + "RegisterSubscriberResponse", + "UnregisterSubscriberRequest", + "UnregisterSubscriberResponse", + "ListSubscribersRequest", + "ListSubscribersResponse", + "CustomerEvent", + "EntitlementEvent", + "SubscriberEvent", ) diff --git a/google/cloud/channel_v1/types/service.py b/google/cloud/channel_v1/types/service.py index 381741f..47e6e12 100644 --- a/google/cloud/channel_v1/types/service.py +++ b/google/cloud/channel_v1/types/service.py @@ -78,6 +78,12 @@ "ListPurchasableOffersRequest", "ListPurchasableOffersResponse", "PurchasableOffer", + "RegisterSubscriberRequest", + "RegisterSubscriberResponse", + "UnregisterSubscriberRequest", + "UnregisterSubscriberResponse", + "ListSubscribersRequest", + "ListSubscribersResponse", }, ) @@ -1485,4 +1491,118 @@ class PurchasableOffer(proto.Message): offer = proto.Field(proto.MESSAGE, number=1, message=gcc_offers.Offer,) +class RegisterSubscriberRequest(proto.Message): + r"""Request Message for RegisterSubscriber. + + Attributes: + account (str): + Required. Resource name of the account. + service_account (str): + Required. Service account which will provide + subscriber access to the registered topic. + """ + + account = proto.Field(proto.STRING, number=1) + + service_account = proto.Field(proto.STRING, number=2) + + +class RegisterSubscriberResponse(proto.Message): + r"""Response Message for RegisterSubscriber. + + Attributes: + topic (str): + Name of the topic to which the subscriber + will listen to. + """ + + topic = proto.Field(proto.STRING, number=1) + + +class UnregisterSubscriberRequest(proto.Message): + r"""Request Message for UnregisterSubscriber. + + Attributes: + account (str): + Required. Resource name of the account. + service_account (str): + Required. Service account which will be + unregistered from getting subscriber access to + the topic. + """ + + account = proto.Field(proto.STRING, number=1) + + service_account = proto.Field(proto.STRING, number=2) + + +class UnregisterSubscriberResponse(proto.Message): + r"""Response Message for UnregisterSubscriber. + + Attributes: + topic (str): + Name of the topic from which the service + account subscriber access has been removed. + """ + + topic = proto.Field(proto.STRING, number=1) + + +class ListSubscribersRequest(proto.Message): + r"""Request Message for ListSubscribers. + + Attributes: + account (str): + Required. Resource name of the account. + page_size (int): + Optional. The maximum number of service + accounts to return. The service may return fewer + than this value. If unspecified, at most 100 + service accounts will be returned. The maximum + value is 1000; values above 1000 will be coerced + to 1000. + page_token (str): + Optional. A page token, received from a previous + ``ListSubscribers`` call. Provide this to retrieve the + subsequent page. + + When paginating, all other parameters provided to + ``ListSubscribers`` must match the call that provided the + page token. + """ + + account = proto.Field(proto.STRING, number=1) + + page_size = proto.Field(proto.INT32, number=2) + + page_token = proto.Field(proto.STRING, number=3) + + +class ListSubscribersResponse(proto.Message): + r"""Response Message for ListSubscribers. + + Attributes: + topic (str): + Name of the topic registered with the + reseller. + service_accounts (Sequence[str]): + List of service accounts which have + subscriber access to the topic. + next_page_token (str): + A token that can be sent as ``page_token`` to retrieve the + next page. If this field is omitted, there are no subsequent + pages. + """ + + @property + def raw_page(self): + return self + + topic = proto.Field(proto.STRING, number=1) + + service_accounts = proto.RepeatedField(proto.STRING, number=2) + + next_page_token = proto.Field(proto.STRING, number=3) + + __all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/google/cloud/channel_v1/types/subscriber_event.py b/google/cloud/channel_v1/types/subscriber_event.py new file mode 100644 index 0000000..ff51579 --- /dev/null +++ b/google/cloud/channel_v1/types/subscriber_event.py @@ -0,0 +1,103 @@ +# -*- 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. +# + +import proto # type: ignore + + +__protobuf__ = proto.module( + package="google.cloud.channel.v1", + manifest={"CustomerEvent", "EntitlementEvent", "SubscriberEvent",}, +) + + +class CustomerEvent(proto.Message): + r"""Represents Pub/Sub message content describing customer + update. + + Attributes: + customer (str): + Resource name of the customer. Format: + accounts/{account_id}/customers/{customer_id} + event_type (google.cloud.channel_v1.types.CustomerEvent.Type): + Type of event which happened on the customer. + """ + + class Type(proto.Enum): + r"""Type of customer event.""" + TYPE_UNSPECIFIED = 0 + + customer = proto.Field(proto.STRING, number=1) + + event_type = proto.Field(proto.ENUM, number=2, enum=Type,) + + +class EntitlementEvent(proto.Message): + r"""Represents Pub/Sub message content describing entitlement + update. + + Attributes: + entitlement (str): + Resource name of an entitlement of the form: + accounts/{account_id}/customers/{customer_id}/entitlements/{entitlement_id} + event_type (google.cloud.channel_v1.types.EntitlementEvent.Type): + Type of event which happened on the + entitlement. + """ + + class Type(proto.Enum): + r"""Type of entitlement event.""" + TYPE_UNSPECIFIED = 0 + CREATED = 1 + PRICE_PLAN_SWITCHED = 3 + COMMITMENT_CHANGED = 4 + RENEWED = 5 + SUSPENDED = 6 + ACTIVATED = 7 + CANCELLED = 8 + SKU_CHANGED = 9 + RENEWAL_SETTING_CHANGED = 10 + PAID_SERVICE_STARTED = 11 + LICENSE_ASSIGNMENT_CHANGED = 12 + + entitlement = proto.Field(proto.STRING, number=1) + + event_type = proto.Field(proto.ENUM, number=2, enum=Type,) + + +class SubscriberEvent(proto.Message): + r"""Represents information which resellers will get as part of + notification from Cloud Pub/Sub. + + Attributes: + customer_event (google.cloud.channel_v1.types.CustomerEvent): + Customer event send as part of Pub/Sub event + to partners. + entitlement_event (google.cloud.channel_v1.types.EntitlementEvent): + Entitlement event send as part of Pub/Sub + event to partners. + """ + + customer_event = proto.Field( + proto.MESSAGE, number=1, oneof="event", message="CustomerEvent", + ) + + entitlement_event = proto.Field( + proto.MESSAGE, number=2, oneof="event", message="EntitlementEvent", + ) + + +__all__ = tuple(sorted(__protobuf__.manifest)) diff --git a/scripts/fixup_channel_v1_keywords.py b/scripts/fixup_channel_v1_keywords.py index 5ab491a..2e11568 100644 --- a/scripts/fixup_channel_v1_keywords.py +++ b/scripts/fixup_channel_v1_keywords.py @@ -62,13 +62,16 @@ class channelCallTransformer(cst.CSTTransformer): 'list_purchasable_offers': ('customer', 'create_entitlement_purchase', 'change_offer_purchase', 'page_size', 'page_token', 'language_code', ), 'list_purchasable_skus': ('customer', 'create_entitlement_purchase', 'change_offer_purchase', 'page_size', 'page_token', 'language_code', ), 'list_skus': ('parent', 'account', 'page_size', 'page_token', 'language_code', ), + 'list_subscribers': ('account', 'page_size', 'page_token', ), 'list_transferable_offers': ('parent', 'sku', 'cloud_identity_id', 'customer_name', 'page_size', 'page_token', 'language_code', ), 'list_transferable_skus': ('parent', 'cloud_identity_id', 'customer_name', 'page_size', 'page_token', 'auth_token', 'language_code', ), 'provision_cloud_identity': ('customer', 'cloud_identity_info', 'user', 'validate_only', ), + 'register_subscriber': ('account', 'service_account', ), 'start_paid_service': ('name', 'request_id', ), 'suspend_entitlement': ('name', 'request_id', ), 'transfer_entitlements': ('parent', 'entitlements', 'auth_token', 'request_id', ), 'transfer_entitlements_to_google': ('parent', 'entitlements', 'request_id', ), + 'unregister_subscriber': ('account', 'service_account', ), 'update_channel_partner_link': ('name', 'channel_partner_link', 'update_mask', ), 'update_customer': ('customer', 'update_mask', ), diff --git a/synth.metadata b/synth.metadata index 504b4d9..53c3236 100644 --- a/synth.metadata +++ b/synth.metadata @@ -4,15 +4,15 @@ "git": { "name": ".", "remote": "https://github.com/googleapis/python-channel.git", - "sha": "6d0a2da2130c68fb4091fd1245ada5adabbce132" + "sha": "0d25fa6ad0e3fac43b7473523393c22cbd157129" } }, { "git": { "name": "googleapis", "remote": "https://github.com/googleapis/googleapis.git", - "sha": "b241bbc1ad920771707fe03e4cedd4e8c492e143", - "internalRef": "350670798" + "sha": "6ef9eaea379fc1cc0355e06a5a20b594543ee693", + "internalRef": "355059873" } }, { @@ -122,6 +122,7 @@ "google/cloud/channel_v1/types/operations.py", "google/cloud/channel_v1/types/products.py", "google/cloud/channel_v1/types/service.py", + "google/cloud/channel_v1/types/subscriber_event.py", "mypy.ini", "noxfile.py", "renovate.json", diff --git a/tests/unit/gapic/channel_v1/test_cloud_channel_service.py b/tests/unit/gapic/channel_v1/test_cloud_channel_service.py index f6f82be..1772cc1 100644 --- a/tests/unit/gapic/channel_v1/test_cloud_channel_service.py +++ b/tests/unit/gapic/channel_v1/test_cloud_channel_service.py @@ -195,7 +195,7 @@ def test_cloud_channel_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, ) @@ -211,7 +211,7 @@ def test_cloud_channel_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, ) @@ -227,7 +227,7 @@ def test_cloud_channel_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, ) @@ -255,7 +255,7 @@ def test_cloud_channel_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, ) @@ -316,29 +316,25 @@ def test_cloud_channel_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. @@ -347,66 +343,53 @@ def test_cloud_channel_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", @@ -436,7 +419,7 @@ def test_cloud_channel_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, ) @@ -470,7 +453,7 @@ def test_cloud_channel_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, ) @@ -489,7 +472,7 @@ def test_cloud_channel_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, ) @@ -6217,6 +6200,538 @@ async def test_list_purchasable_offers_async_pages(): assert page_.raw_page.next_page_token == token +def test_register_subscriber( + transport: str = "grpc", request_type=service.RegisterSubscriberRequest +): + client = CloudChannelServiceClient( + credentials=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.register_subscriber), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = service.RegisterSubscriberResponse(topic="topic_value",) + + response = client.register_subscriber(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == service.RegisterSubscriberRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, service.RegisterSubscriberResponse) + + assert response.topic == "topic_value" + + +def test_register_subscriber_from_dict(): + test_register_subscriber(request_type=dict) + + +@pytest.mark.asyncio +async def test_register_subscriber_async( + transport: str = "grpc_asyncio", request_type=service.RegisterSubscriberRequest +): + client = CloudChannelServiceAsyncClient( + credentials=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.register_subscriber), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + service.RegisterSubscriberResponse(topic="topic_value",) + ) + + response = await client.register_subscriber(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == service.RegisterSubscriberRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, service.RegisterSubscriberResponse) + + assert response.topic == "topic_value" + + +@pytest.mark.asyncio +async def test_register_subscriber_async_from_dict(): + await test_register_subscriber_async(request_type=dict) + + +def test_register_subscriber_field_headers(): + client = CloudChannelServiceClient(credentials=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 = service.RegisterSubscriberRequest() + request.account = "account/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.register_subscriber), "__call__" + ) as call: + call.return_value = service.RegisterSubscriberResponse() + + client.register_subscriber(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", "account=account/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_register_subscriber_field_headers_async(): + client = CloudChannelServiceAsyncClient( + credentials=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 = service.RegisterSubscriberRequest() + request.account = "account/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.register_subscriber), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + service.RegisterSubscriberResponse() + ) + + await client.register_subscriber(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", "account=account/value",) in kw["metadata"] + + +def test_unregister_subscriber( + transport: str = "grpc", request_type=service.UnregisterSubscriberRequest +): + client = CloudChannelServiceClient( + credentials=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.unregister_subscriber), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = service.UnregisterSubscriberResponse(topic="topic_value",) + + response = client.unregister_subscriber(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == service.UnregisterSubscriberRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, service.UnregisterSubscriberResponse) + + assert response.topic == "topic_value" + + +def test_unregister_subscriber_from_dict(): + test_unregister_subscriber(request_type=dict) + + +@pytest.mark.asyncio +async def test_unregister_subscriber_async( + transport: str = "grpc_asyncio", request_type=service.UnregisterSubscriberRequest +): + client = CloudChannelServiceAsyncClient( + credentials=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.unregister_subscriber), "__call__" + ) as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + service.UnregisterSubscriberResponse(topic="topic_value",) + ) + + response = await client.unregister_subscriber(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == service.UnregisterSubscriberRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, service.UnregisterSubscriberResponse) + + assert response.topic == "topic_value" + + +@pytest.mark.asyncio +async def test_unregister_subscriber_async_from_dict(): + await test_unregister_subscriber_async(request_type=dict) + + +def test_unregister_subscriber_field_headers(): + client = CloudChannelServiceClient(credentials=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 = service.UnregisterSubscriberRequest() + request.account = "account/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.unregister_subscriber), "__call__" + ) as call: + call.return_value = service.UnregisterSubscriberResponse() + + client.unregister_subscriber(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", "account=account/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_unregister_subscriber_field_headers_async(): + client = CloudChannelServiceAsyncClient( + credentials=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 = service.UnregisterSubscriberRequest() + request.account = "account/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.unregister_subscriber), "__call__" + ) as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + service.UnregisterSubscriberResponse() + ) + + await client.unregister_subscriber(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", "account=account/value",) in kw["metadata"] + + +def test_list_subscribers( + transport: str = "grpc", request_type=service.ListSubscribersRequest +): + client = CloudChannelServiceClient( + credentials=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.list_subscribers), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = service.ListSubscribersResponse( + topic="topic_value", + service_accounts=["service_accounts_value"], + next_page_token="next_page_token_value", + ) + + response = client.list_subscribers(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + + assert args[0] == service.ListSubscribersRequest() + + # Establish that the response is the type that we expect. + + assert isinstance(response, pagers.ListSubscribersPager) + + assert response.topic == "topic_value" + + assert response.service_accounts == ["service_accounts_value"] + + assert response.next_page_token == "next_page_token_value" + + +def test_list_subscribers_from_dict(): + test_list_subscribers(request_type=dict) + + +@pytest.mark.asyncio +async def test_list_subscribers_async( + transport: str = "grpc_asyncio", request_type=service.ListSubscribersRequest +): + client = CloudChannelServiceAsyncClient( + credentials=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.list_subscribers), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + service.ListSubscribersResponse( + topic="topic_value", + service_accounts=["service_accounts_value"], + next_page_token="next_page_token_value", + ) + ) + + response = await client.list_subscribers(request) + + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) + _, args, _ = call.mock_calls[0] + + assert args[0] == service.ListSubscribersRequest() + + # Establish that the response is the type that we expect. + assert isinstance(response, pagers.ListSubscribersAsyncPager) + + assert response.topic == "topic_value" + + assert response.service_accounts == ["service_accounts_value"] + + assert response.next_page_token == "next_page_token_value" + + +@pytest.mark.asyncio +async def test_list_subscribers_async_from_dict(): + await test_list_subscribers_async(request_type=dict) + + +def test_list_subscribers_field_headers(): + client = CloudChannelServiceClient(credentials=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 = service.ListSubscribersRequest() + request.account = "account/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_subscribers), "__call__") as call: + call.return_value = service.ListSubscribersResponse() + + client.list_subscribers(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", "account=account/value",) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_list_subscribers_field_headers_async(): + client = CloudChannelServiceAsyncClient( + credentials=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 = service.ListSubscribersRequest() + request.account = "account/value" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_subscribers), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + service.ListSubscribersResponse() + ) + + await client.list_subscribers(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", "account=account/value",) in kw["metadata"] + + +def test_list_subscribers_pager(): + client = CloudChannelServiceClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_subscribers), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + service.ListSubscribersResponse( + service_accounts=[str(), str(), str(),], next_page_token="abc", + ), + service.ListSubscribersResponse( + service_accounts=[], next_page_token="def", + ), + service.ListSubscribersResponse( + service_accounts=[str(),], next_page_token="ghi", + ), + service.ListSubscribersResponse(service_accounts=[str(), str(),],), + RuntimeError, + ) + + metadata = () + metadata = tuple(metadata) + ( + gapic_v1.routing_header.to_grpc_metadata((("account", ""),)), + ) + pager = client.list_subscribers(request={}) + + assert pager._metadata == metadata + + results = [i for i in pager] + assert len(results) == 6 + assert all(isinstance(i, str) for i in results) + + +def test_list_subscribers_pages(): + client = CloudChannelServiceClient(credentials=credentials.AnonymousCredentials,) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.list_subscribers), "__call__") as call: + # Set the response to a series of pages. + call.side_effect = ( + service.ListSubscribersResponse( + service_accounts=[str(), str(), str(),], next_page_token="abc", + ), + service.ListSubscribersResponse( + service_accounts=[], next_page_token="def", + ), + service.ListSubscribersResponse( + service_accounts=[str(),], next_page_token="ghi", + ), + service.ListSubscribersResponse(service_accounts=[str(), str(),],), + RuntimeError, + ) + pages = list(client.list_subscribers(request={}).pages) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + +@pytest.mark.asyncio +async def test_list_subscribers_async_pager(): + client = CloudChannelServiceAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_subscribers), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + service.ListSubscribersResponse( + service_accounts=[str(), str(), str(),], next_page_token="abc", + ), + service.ListSubscribersResponse( + service_accounts=[], next_page_token="def", + ), + service.ListSubscribersResponse( + service_accounts=[str(),], next_page_token="ghi", + ), + service.ListSubscribersResponse(service_accounts=[str(), str(),],), + RuntimeError, + ) + async_pager = await client.list_subscribers(request={},) + assert async_pager.next_page_token == "abc" + responses = [] + async for response in async_pager: + responses.append(response) + + assert len(responses) == 6 + assert all(isinstance(i, str) for i in responses) + + +@pytest.mark.asyncio +async def test_list_subscribers_async_pages(): + client = CloudChannelServiceAsyncClient( + credentials=credentials.AnonymousCredentials, + ) + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object( + type(client.transport.list_subscribers), "__call__", new_callable=mock.AsyncMock + ) as call: + # Set the response to a series of pages. + call.side_effect = ( + service.ListSubscribersResponse( + service_accounts=[str(), str(), str(),], next_page_token="abc", + ), + service.ListSubscribersResponse( + service_accounts=[], next_page_token="def", + ), + service.ListSubscribersResponse( + service_accounts=[str(),], next_page_token="ghi", + ), + service.ListSubscribersResponse(service_accounts=[str(), str(),],), + RuntimeError, + ) + pages = [] + async for page_ in (await client.list_subscribers(request={})).pages: + pages.append(page_) + for page_, token in zip(pages, ["abc", "def", "ghi", ""]): + assert page_.raw_page.next_page_token == token + + def test_credentials_transport_error(): # It is an error to provide credentials and a transport instance. transport = transports.CloudChannelServiceGrpcTransport( @@ -6344,6 +6859,9 @@ def test_cloud_channel_service_base_transport(): "list_offers", "list_purchasable_skus", "list_purchasable_offers", + "register_subscriber", + "unregister_subscriber", + "list_subscribers", ) for method in methods: with pytest.raises(NotImplementedError): @@ -6410,6 +6928,53 @@ def test_cloud_channel_service_transport_auth_adc(): ) +@pytest.mark.parametrize( + "transport_class", + [ + transports.CloudChannelServiceGrpcTransport, + transports.CloudChannelServiceGrpcAsyncIOTransport, + ], +) +def test_cloud_channel_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/apps.order",), + 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_cloud_channel_service_host_no_port(): client = CloudChannelServiceClient( credentials=credentials.AnonymousCredentials(), @@ -6454,6 +7019,8 @@ def test_cloud_channel_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", [ @@ -6506,6 +7073,8 @@ def test_cloud_channel_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", [