diff --git a/google/cloud/iam_v2beta/services/policies/async_client.py b/google/cloud/iam_v2beta/services/policies/async_client.py index 254635f..a56b005 100644 --- a/google/cloud/iam_v2beta/services/policies/async_client.py +++ b/google/cloud/iam_v2beta/services/policies/async_client.py @@ -33,6 +33,7 @@ from google.api_core import operation # type: ignore from google.api_core import operation_async # type: ignore +from google.longrunning import operations_pb2 from google.protobuf import timestamp_pb2 # type: ignore from google.cloud.iam_v2beta.services.policies import pagers @@ -54,8 +55,6 @@ class PoliciesAsyncClient: DEFAULT_ENDPOINT = PoliciesClient.DEFAULT_ENDPOINT DEFAULT_MTLS_ENDPOINT = PoliciesClient.DEFAULT_MTLS_ENDPOINT - policy_path = staticmethod(PoliciesClient.policy_path) - parse_policy_path = staticmethod(PoliciesClient.parse_policy_path) common_billing_account_path = staticmethod( PoliciesClient.common_billing_account_path ) @@ -831,6 +830,60 @@ async def sample_delete_policy(): # Done; return the response. return response + async def get_operation( + self, + request: operations_pb2.GetOperationRequest = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operations_pb2.Operation: + r"""Gets the latest state of a long-running operation. + + Args: + request (:class:`~.operations_pb2.GetOperationRequest`): + The request object. Request message for + `GetOperation` method. + retry (google.api_core.retry.Retry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + Returns: + ~.operations_pb2.Operation: + An ``Operation`` object. + """ + # Create or coerce a protobuf request object. + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = operations_pb2.GetOperationRequest(**request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method.wrap_method( + self._client._transport.get_operation, + 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 __aenter__(self): return self diff --git a/google/cloud/iam_v2beta/services/policies/client.py b/google/cloud/iam_v2beta/services/policies/client.py index d70c007..430c477 100644 --- a/google/cloud/iam_v2beta/services/policies/client.py +++ b/google/cloud/iam_v2beta/services/policies/client.py @@ -36,6 +36,7 @@ from google.api_core import operation # type: ignore from google.api_core import operation_async # type: ignore +from google.longrunning import operations_pb2 from google.protobuf import timestamp_pb2 # type: ignore from google.cloud.iam_v2beta.services.policies import pagers @@ -168,21 +169,6 @@ def transport(self) -> PoliciesTransport: """ return self._transport - @staticmethod - def policy_path( - policy: str, - ) -> str: - """Returns a fully-qualified policy string.""" - return "policies/{policy}".format( - policy=policy, - ) - - @staticmethod - def parse_policy_path(path: str) -> Dict[str, str]: - """Parses a policy path into its component segments.""" - m = re.match(r"^policies/(?P.+?)$", path) - return m.groupdict() if m else {} - @staticmethod def common_billing_account_path( billing_account: str, @@ -1024,6 +1010,60 @@ def __exit__(self, type, value, traceback): """ self.transport.close() + def get_operation( + self, + request: operations_pb2.GetOperationRequest = None, + *, + retry: OptionalRetry = gapic_v1.method.DEFAULT, + timeout: float = None, + metadata: Sequence[Tuple[str, str]] = (), + ) -> operations_pb2.Operation: + r"""Gets the latest state of a long-running operation. + + Args: + request (:class:`~.operations_pb2.GetOperationRequest`): + The request object. Request message for + `GetOperation` method. + retry (google.api_core.retry.Retry): Designation of what errors, + if any, should be retried. + timeout (float): The timeout for this request. + metadata (Sequence[Tuple[str, str]]): Strings which should be + sent along with the request as metadata. + Returns: + ~.operations_pb2.Operation: + An ``Operation`` object. + """ + # Create or coerce a protobuf request object. + # The request isn't a proto-plus wrapped type, + # so it must be constructed via keyword expansion. + if isinstance(request, dict): + request = operations_pb2.GetOperationRequest(**request) + + # Wrap the RPC method; this adds retry and timeout information, + # and friendly error handling. + rpc = gapic_v1.method.wrap_method( + self._transport.get_operation, + 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 = 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/iam_v2beta/services/policies/transports/base.py b/google/cloud/iam_v2beta/services/policies/transports/base.py index 82e5648..c00cc4b 100644 --- a/google/cloud/iam_v2beta/services/policies/transports/base.py +++ b/google/cloud/iam_v2beta/services/policies/transports/base.py @@ -259,6 +259,15 @@ def delete_policy( ]: raise NotImplementedError() + @property + def get_operation( + self, + ) -> Callable[ + [operations_pb2.GetOperationRequest], + Union[operations_pb2.Operation, Awaitable[operations_pb2.Operation]], + ]: + raise NotImplementedError() + @property def kind(self) -> str: raise NotImplementedError() diff --git a/google/cloud/iam_v2beta/services/policies/transports/grpc.py b/google/cloud/iam_v2beta/services/policies/transports/grpc.py index e0f4471..3cd60a7 100644 --- a/google/cloud/iam_v2beta/services/policies/transports/grpc.py +++ b/google/cloud/iam_v2beta/services/policies/transports/grpc.py @@ -393,6 +393,23 @@ def delete_policy( def close(self): self.grpc_channel.close() + @property + def get_operation( + self, + ) -> Callable[[operations_pb2.GetOperationRequest], operations_pb2.Operation]: + r"""Return a callable for the get_operation method over gRPC.""" + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_operation" not in self._stubs: + self._stubs["get_operation"] = self.grpc_channel.unary_unary( + "/google.longrunning.Operations/GetOperation", + request_serializer=operations_pb2.GetOperationRequest.SerializeToString, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["get_operation"] + @property def kind(self) -> str: return "grpc" diff --git a/google/cloud/iam_v2beta/services/policies/transports/grpc_asyncio.py b/google/cloud/iam_v2beta/services/policies/transports/grpc_asyncio.py index 0e03360..85ebabb 100644 --- a/google/cloud/iam_v2beta/services/policies/transports/grpc_asyncio.py +++ b/google/cloud/iam_v2beta/services/policies/transports/grpc_asyncio.py @@ -400,5 +400,22 @@ def delete_policy( def close(self): return self.grpc_channel.close() + @property + def get_operation( + self, + ) -> Callable[[operations_pb2.GetOperationRequest], operations_pb2.Operation]: + r"""Return a callable for the get_operation method over gRPC.""" + # Generate a "stub function" on-the-fly which will actually make + # the request. + # gRPC handles serialization and deserialization, so we just need + # to pass in the functions for each. + if "get_operation" not in self._stubs: + self._stubs["get_operation"] = self.grpc_channel.unary_unary( + "/google.longrunning.Operations/GetOperation", + request_serializer=operations_pb2.GetOperationRequest.SerializeToString, + response_deserializer=operations_pb2.Operation.FromString, + ) + return self._stubs["get_operation"] + __all__ = ("PoliciesGrpcAsyncIOTransport",) diff --git a/google/cloud/iam_v2beta/types/deny.py b/google/cloud/iam_v2beta/types/deny.py index 7438334..15f277d 100644 --- a/google/cloud/iam_v2beta/types/deny.py +++ b/google/cloud/iam_v2beta/types/deny.py @@ -34,8 +34,9 @@ class DenyRule(proto.Message): contain the following values: - ``principalSet://goog/public:all``: A special identifier - that represents any user who is on the internet, even if - they do not have a Google Account or are not logged in. + that represents any principal that is on the internet, + even if they do not have a Google Account or are not + logged in. - ``principal://goog/subject/{email_id}``: A specific Google Account. Includes Gmail, Cloud Identity, and diff --git a/tests/unit/gapic/iam_v2beta/test_policies.py b/tests/unit/gapic/iam_v2beta/test_policies.py index 6c1aa76..b56a6da 100644 --- a/tests/unit/gapic/iam_v2beta/test_policies.py +++ b/tests/unit/gapic/iam_v2beta/test_policies.py @@ -2080,6 +2080,7 @@ def test_policies_base_transport(): "create_policy", "update_policy", "delete_policy", + "get_operation", ) for method in methods: with pytest.raises(NotImplementedError): @@ -2451,28 +2452,8 @@ def test_policies_grpc_lro_async_client(): assert transport.operations_client is transport.operations_client -def test_policy_path(): - policy = "squid" - expected = "policies/{policy}".format( - policy=policy, - ) - actual = PoliciesClient.policy_path(policy) - assert expected == actual - - -def test_parse_policy_path(): - expected = { - "policy": "clam", - } - path = PoliciesClient.policy_path(**expected) - - # Check that the path construction is reversible. - actual = PoliciesClient.parse_policy_path(path) - assert expected == actual - - def test_common_billing_account_path(): - billing_account = "whelk" + billing_account = "squid" expected = "billingAccounts/{billing_account}".format( billing_account=billing_account, ) @@ -2482,7 +2463,7 @@ def test_common_billing_account_path(): def test_parse_common_billing_account_path(): expected = { - "billing_account": "octopus", + "billing_account": "clam", } path = PoliciesClient.common_billing_account_path(**expected) @@ -2492,7 +2473,7 @@ def test_parse_common_billing_account_path(): def test_common_folder_path(): - folder = "oyster" + folder = "whelk" expected = "folders/{folder}".format( folder=folder, ) @@ -2502,7 +2483,7 @@ def test_common_folder_path(): def test_parse_common_folder_path(): expected = { - "folder": "nudibranch", + "folder": "octopus", } path = PoliciesClient.common_folder_path(**expected) @@ -2512,7 +2493,7 @@ def test_parse_common_folder_path(): def test_common_organization_path(): - organization = "cuttlefish" + organization = "oyster" expected = "organizations/{organization}".format( organization=organization, ) @@ -2522,7 +2503,7 @@ def test_common_organization_path(): def test_parse_common_organization_path(): expected = { - "organization": "mussel", + "organization": "nudibranch", } path = PoliciesClient.common_organization_path(**expected) @@ -2532,7 +2513,7 @@ def test_parse_common_organization_path(): def test_common_project_path(): - project = "winkle" + project = "cuttlefish" expected = "projects/{project}".format( project=project, ) @@ -2542,7 +2523,7 @@ def test_common_project_path(): def test_parse_common_project_path(): expected = { - "project": "nautilus", + "project": "mussel", } path = PoliciesClient.common_project_path(**expected) @@ -2552,8 +2533,8 @@ def test_parse_common_project_path(): def test_common_location_path(): - project = "scallop" - location = "abalone" + project = "winkle" + location = "nautilus" expected = "projects/{project}/locations/{location}".format( project=project, location=location, @@ -2564,8 +2545,8 @@ def test_common_location_path(): def test_parse_common_location_path(): expected = { - "project": "squid", - "location": "clam", + "project": "scallop", + "location": "abalone", } path = PoliciesClient.common_location_path(**expected) @@ -2611,6 +2592,151 @@ async def test_transport_close_async(): close.assert_called_once() +def test_get_operation(transport: str = "grpc"): + client = PoliciesClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = operations_pb2.GetOperationRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation() + response = client.get_operation(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, operations_pb2.Operation) + + +@pytest.mark.asyncio +async def test_get_operation_async(transport: str = "grpc"): + client = PoliciesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + transport=transport, + ) + + # Everything is optional in proto3 as far as the runtime is concerned, + # and we are mocking out the actual API, so just send an empty request. + request = operations_pb2.GetOperationRequest() + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation() + ) + response = await client.get_operation(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the response is the type that we expect. + assert isinstance(response, operations_pb2.Operation) + + +def test_get_operation_field_headers(): + client = PoliciesClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = operations_pb2.GetOperationRequest() + request.name = "locations" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + call.return_value = operations_pb2.Operation() + + client.get_operation(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=locations", + ) in kw["metadata"] + + +@pytest.mark.asyncio +async def test_get_operation_field_headers_async(): + client = PoliciesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + + # Any value that is part of the HTTP/1.1 URI should be sent as + # a field header. Set these to a non-empty value. + request = operations_pb2.GetOperationRequest() + request.name = "locations" + + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation() + ) + await client.get_operation(request) + # Establish that the underlying gRPC stub method was called. + assert len(call.mock_calls) == 1 + _, args, _ = call.mock_calls[0] + assert args[0] == request + + # Establish that the field header was sent. + _, _, kw = call.mock_calls[0] + assert ( + "x-goog-request-params", + "name=locations", + ) in kw["metadata"] + + +def test_get_operation_from_dict(): + client = PoliciesClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = operations_pb2.Operation() + + response = client.get_operation( + request={ + "name": "locations", + } + ) + call.assert_called() + + +@pytest.mark.asyncio +async def test_get_operation_from_dict_async(): + client = PoliciesAsyncClient( + credentials=ga_credentials.AnonymousCredentials(), + ) + # Mock the actual call within the gRPC stub, and fake the request. + with mock.patch.object(type(client.transport.get_operation), "__call__") as call: + # Designate an appropriate return value for the call. + call.return_value = grpc_helpers_async.FakeUnaryUnaryCall( + operations_pb2.Operation() + ) + response = await client.get_operation( + request={ + "name": "locations", + } + ) + call.assert_called() + + def test_transport_close(): transports = { "grpc": "_grpc_channel",