diff --git a/logging/google/cloud/logging/_gax.py b/logging/google/cloud/logging/_gax.py index 5e096841324c4..1df2be5daf70e 100644 --- a/logging/google/cloud/logging/_gax.py +++ b/logging/google/cloud/logging/_gax.py @@ -16,6 +16,12 @@ import functools +from google.cloud.gapic.logging.v2.config_service_v2_api import ( + ConfigServiceV2Api) +from google.cloud.gapic.logging.v2.logging_service_v2_api import ( + LoggingServiceV2Api) +from google.cloud.gapic.logging.v2.metrics_service_v2_api import ( + MetricsServiceV2Api) from google.gax import CallOptions from google.gax import INITIAL_PAGE from google.gax.errors import GaxError @@ -28,6 +34,8 @@ from grpc import StatusCode from google.cloud._helpers import _datetime_to_rfc3339 +from google.cloud._helpers import make_secure_channel +from google.cloud.connection import DEFAULT_USER_AGENT from google.cloud.exceptions import Conflict from google.cloud.exceptions import NotFound from google.cloud.iterator import GAXIterator @@ -511,3 +519,51 @@ def _item_to_metric(iterator, log_metric_pb): """ resource = MessageToDict(log_metric_pb) return Metric.from_api_repr(resource, iterator.client) + + +def make_gax_logging_api(client): + """Create an instance of the GAX Logging API. + + :type client: :class:`~google.cloud.logging.client.Client` + :param client: The client that holds configuration details. + + :rtype: :class:`_LoggingAPI` + :returns: A metrics API instance with the proper credentials. + """ + channel = make_secure_channel( + client.connection.credentials, DEFAULT_USER_AGENT, + LoggingServiceV2Api.SERVICE_ADDRESS) + generated = LoggingServiceV2Api(channel=channel) + return _LoggingAPI(generated, client) + + +def make_gax_metrics_api(client): + """Create an instance of the GAX Metrics API. + + :type client: :class:`~google.cloud.logging.client.Client` + :param client: The client that holds configuration details. + + :rtype: :class:`_MetricsAPI` + :returns: A metrics API instance with the proper credentials. + """ + channel = make_secure_channel( + client.connection.credentials, DEFAULT_USER_AGENT, + MetricsServiceV2Api.SERVICE_ADDRESS) + generated = MetricsServiceV2Api(channel=channel) + return _MetricsAPI(generated, client) + + +def make_gax_sinks_api(client): + """Create an instance of the GAX Sinks API. + + :type client: :class:`~google.cloud.logging.client.Client` + :param client: The client that holds configuration details. + + :rtype: :class:`_SinksAPI` + :returns: A metrics API instance with the proper credentials. + """ + channel = make_secure_channel( + client.connection.credentials, DEFAULT_USER_AGENT, + ConfigServiceV2Api.SERVICE_ADDRESS) + generated = ConfigServiceV2Api(channel=channel) + return _SinksAPI(generated, client) diff --git a/logging/google/cloud/logging/client.py b/logging/google/cloud/logging/client.py index ec3cf8174719d..801d4ec63dc87 100644 --- a/logging/google/cloud/logging/client.py +++ b/logging/google/cloud/logging/client.py @@ -17,20 +17,14 @@ import os try: - from google.cloud.gapic.logging.v2.config_service_v2_api import ( - ConfigServiceV2Api as GeneratedSinksAPI) - from google.cloud.gapic.logging.v2.logging_service_v2_api import ( - LoggingServiceV2Api as GeneratedLoggingAPI) - from google.cloud.gapic.logging.v2.metrics_service_v2_api import ( - MetricsServiceV2Api as GeneratedMetricsAPI) - from google.cloud.logging._gax import _LoggingAPI as GAXLoggingAPI - from google.cloud.logging._gax import _MetricsAPI as GAXMetricsAPI - from google.cloud.logging._gax import _SinksAPI as GAXSinksAPI + from google.cloud.logging._gax import make_gax_logging_api + from google.cloud.logging._gax import make_gax_metrics_api + from google.cloud.logging._gax import make_gax_sinks_api except ImportError: # pragma: NO COVER _HAVE_GAX = False - GeneratedLoggingAPI = GAXLoggingAPI = None - GeneratedMetricsAPI = GAXMetricsAPI = None - GeneratedSinksAPI = GAXSinksAPI = None + make_gax_logging_api = None + make_gax_metrics_api = None + make_gax_sinks_api = None else: _HAVE_GAX = True @@ -97,8 +91,7 @@ def logging_api(self): """ if self._logging_api is None: if self._use_gax: - generated = GeneratedLoggingAPI() - self._logging_api = GAXLoggingAPI(generated, self) + self._logging_api = make_gax_logging_api(self) else: self._logging_api = JSONLoggingAPI(self) return self._logging_api @@ -112,8 +105,7 @@ def sinks_api(self): """ if self._sinks_api is None: if self._use_gax: - generated = GeneratedSinksAPI() - self._sinks_api = GAXSinksAPI(generated, self) + self._sinks_api = make_gax_sinks_api(self) else: self._sinks_api = JSONSinksAPI(self) return self._sinks_api @@ -127,8 +119,7 @@ def metrics_api(self): """ if self._metrics_api is None: if self._use_gax: - generated = GeneratedMetricsAPI() - self._metrics_api = GAXMetricsAPI(generated, self) + self._metrics_api = make_gax_metrics_api(self) else: self._metrics_api = JSONMetricsAPI(self) return self._metrics_api diff --git a/logging/unit_tests/test__gax.py b/logging/unit_tests/test__gax.py index 90206e26a388c..7c79b2c1c10b6 100644 --- a/logging/unit_tests/test__gax.py +++ b/logging/unit_tests/test__gax.py @@ -1058,6 +1058,132 @@ def test_metric_delete_hit(self): self.assertIsNone(options) +@unittest.skipUnless(_HAVE_GAX, 'No gax-python') +class Test_make_gax_logging_api(unittest.TestCase): + + def _call_fut(self, client): + from google.cloud.logging._gax import make_gax_logging_api + return make_gax_logging_api(client) + + def test_it(self): + from google.cloud._testing import _Monkey + from google.cloud.logging import _gax as MUT + + creds = object() + client = _Client(creds) + channels = [] + channel_args = [] + channel_obj = object() + generated = object() + + def make_channel(*args): + channel_args.append(args) + return channel_obj + + def generated_api(channel=None): + channels.append(channel) + return generated + + host = 'foo.apis.invalid' + generated_api.SERVICE_ADDRESS = host + + with _Monkey(MUT, LoggingServiceV2Api=generated_api, + make_secure_channel=make_channel): + logging_api = self._call_fut(client) + + self.assertEqual(channels, [channel_obj]) + self.assertEqual(channel_args, + [(creds, MUT.DEFAULT_USER_AGENT, host)]) + + self.assertIsInstance(logging_api, MUT._LoggingAPI) + self.assertIs(logging_api._gax_api, generated) + self.assertIs(logging_api._client, client) + + +@unittest.skipUnless(_HAVE_GAX, 'No gax-python') +class Test_make_gax_metrics_api(unittest.TestCase): + + def _call_fut(self, client): + from google.cloud.logging._gax import make_gax_metrics_api + return make_gax_metrics_api(client) + + def test_it(self): + from google.cloud._testing import _Monkey + from google.cloud.logging import _gax as MUT + + creds = object() + client = _Client(creds) + channels = [] + channel_args = [] + channel_obj = object() + generated = object() + + def make_channel(*args): + channel_args.append(args) + return channel_obj + + def generated_api(channel=None): + channels.append(channel) + return generated + + host = 'foo.apis.invalid' + generated_api.SERVICE_ADDRESS = host + + with _Monkey(MUT, MetricsServiceV2Api=generated_api, + make_secure_channel=make_channel): + metrics_api = self._call_fut(client) + + self.assertEqual(channels, [channel_obj]) + self.assertEqual(channel_args, + [(creds, MUT.DEFAULT_USER_AGENT, host)]) + + self.assertIsInstance(metrics_api, MUT._MetricsAPI) + self.assertIs(metrics_api._gax_api, generated) + self.assertIs(metrics_api._client, client) + + +@unittest.skipUnless(_HAVE_GAX, 'No gax-python') +class Test_make_gax_sinks_api(unittest.TestCase): + + def _call_fut(self, client): + from google.cloud.logging._gax import make_gax_sinks_api + return make_gax_sinks_api(client) + + def test_it(self): + from google.cloud._testing import _Monkey + from google.cloud.logging import _gax as MUT + + creds = object() + client = _Client(creds) + channels = [] + channel_args = [] + channel_obj = object() + generated = object() + + def make_channel(*args): + channel_args.append(args) + return channel_obj + + def generated_api(channel=None): + channels.append(channel) + return generated + + host = 'foo.apis.invalid' + generated_api.SERVICE_ADDRESS = host + + with _Monkey(MUT, ConfigServiceV2Api=generated_api, + make_secure_channel=make_channel): + sinks_api = self._call_fut(client) + + self.assertEqual(channels, [channel_obj]) + self.assertEqual(channel_args, + [(creds, MUT.DEFAULT_USER_AGENT, host)]) + + self.assertIsInstance(sinks_api, MUT._SinksAPI) + self.assertIs(sinks_api._gax_api, generated) + self.assertIs(sinks_api._client, client) + + class _GAXLoggingAPI(_GAXBaseAPI): _delete_not_found = False @@ -1172,3 +1298,15 @@ def delete_log_metric(self, metric_name, options=None): raise GaxError('error') if self._log_metric_not_found: raise GaxError('notfound', self._make_grpc_not_found()) + + +class _Connection(object): + + def __init__(self, credentials): + self.credentials = credentials + + +class _Client(object): + + def __init__(self, credentials): + self.connection = _Connection(credentials) diff --git a/logging/unit_tests/test_client.py b/logging/unit_tests/test_client.py index 609d8b40728e6..1b8daa10f9fb9 100644 --- a/logging/unit_tests/test_client.py +++ b/logging/unit_tests/test_client.py @@ -39,12 +39,10 @@ def test_ctor(self): self.assertEqual(client.project, self.PROJECT) def test_logging_api_wo_gax(self): - from google.cloud._testing import _Monkey - from google.cloud.logging import client as MUT from google.cloud.logging.connection import _LoggingAPI - with _Monkey(MUT, _USE_GAX=False): - client = self._makeOne(self.PROJECT, credentials=_Credentials()) + client = self._makeOne(self.PROJECT, credentials=_Credentials(), + use_gax=False) conn = client.connection = object() api = client.logging_api @@ -58,31 +56,22 @@ def test_logging_api_w_gax(self): from google.cloud.logging import client as MUT from google.cloud._testing import _Monkey - wrapped = object() - _called_with = [] - - def _generated_api(*args, **kw): - _called_with.append((args, kw)) - return wrapped + clients = [] + api_obj = object() - class _GaxLoggingAPI(object): - - def __init__(self, _wrapped, client): - self._wrapped = _wrapped - self.client = client + def make_api(client_obj): + clients.append(client_obj) + return api_obj creds = _Credentials() - client = self._makeOne(project=self.PROJECT, credentials=creds) + client = self._makeOne(project=self.PROJECT, credentials=creds, + use_gax=True) - with _Monkey(MUT, - _USE_GAX=True, - GeneratedLoggingAPI=_generated_api, - GAXLoggingAPI=_GaxLoggingAPI): + with _Monkey(MUT, make_gax_logging_api=make_api): api = client.logging_api - self.assertIsInstance(api, _GaxLoggingAPI) - self.assertIs(api._wrapped, wrapped) - self.assertIs(api.client, client) + self.assertIs(api, api_obj) + self.assertEqual(clients, [client]) # API instance is cached again = client.logging_api self.assertIs(again, api) @@ -121,31 +110,22 @@ def test_sinks_api_w_gax(self): from google.cloud.logging import client as MUT from google.cloud._testing import _Monkey - wrapped = object() - _called_with = [] - - def _generated_api(*args, **kw): - _called_with.append((args, kw)) - return wrapped - - class _GaxSinksAPI(object): + clients = [] + api_obj = object() - def __init__(self, _wrapped, client): - self._wrapped = _wrapped - self.client = client + def make_api(client_obj): + clients.append(client_obj) + return api_obj creds = _Credentials() - client = self._makeOne(project=self.PROJECT, credentials=creds) + client = self._makeOne(project=self.PROJECT, credentials=creds, + use_gax=True) - with _Monkey(MUT, - _USE_GAX=True, - GeneratedSinksAPI=_generated_api, - GAXSinksAPI=_GaxSinksAPI): + with _Monkey(MUT, make_gax_sinks_api=make_api): api = client.sinks_api - self.assertIsInstance(api, _GaxSinksAPI) - self.assertIs(api._wrapped, wrapped) - self.assertIs(api.client, client) + self.assertIs(api, api_obj) + self.assertEqual(clients, [client]) # API instance is cached again = client.sinks_api self.assertIs(again, api) @@ -171,31 +151,22 @@ def test_metrics_api_w_gax(self): from google.cloud.logging import client as MUT from google.cloud._testing import _Monkey - wrapped = object() - _called_with = [] + clients = [] + api_obj = object() - def _generated_api(*args, **kw): - _called_with.append((args, kw)) - return wrapped - - class _GaxMetricsAPI(object): - - def __init__(self, _wrapped, client): - self._wrapped = _wrapped - self.client = client + def make_api(client_obj): + clients.append(client_obj) + return api_obj creds = _Credentials() - client = self._makeOne(project=self.PROJECT, credentials=creds) + client = self._makeOne(project=self.PROJECT, credentials=creds, + use_gax=True) - with _Monkey(MUT, - _USE_GAX=True, - GeneratedMetricsAPI=_generated_api, - GAXMetricsAPI=_GaxMetricsAPI): + with _Monkey(MUT, make_gax_metrics_api=make_api): api = client.metrics_api - self.assertIsInstance(api, _GaxMetricsAPI) - self.assertIs(api._wrapped, wrapped) - self.assertIs(api.client, client) + self.assertIs(api, api_obj) + self.assertEqual(clients, [client]) # API instance is cached again = client.metrics_api self.assertIs(again, api) diff --git a/pubsub/google/cloud/pubsub/_gax.py b/pubsub/google/cloud/pubsub/_gax.py index bb700f7018c51..ecfba42477f49 100644 --- a/pubsub/google/cloud/pubsub/_gax.py +++ b/pubsub/google/cloud/pubsub/_gax.py @@ -30,6 +30,8 @@ from google.cloud._helpers import _to_bytes from google.cloud._helpers import _pb_timestamp_to_rfc3339 +from google.cloud._helpers import make_secure_channel +from google.cloud.connection import DEFAULT_USER_AGENT from google.cloud.exceptions import Conflict from google.cloud.exceptions import NotFound from google.cloud.iterator import GAXIterator @@ -518,9 +520,12 @@ def make_gax_publisher_api(connection): configuration. :rtype: :class:`~google.cloud.pubsub.v1.subscriber_api.SubscriberApi` """ - channel = None if connection.in_emulator: channel = insecure_channel(connection.host) + else: + channel = make_secure_channel( + connection.credentials, DEFAULT_USER_AGENT, + PublisherApi.SERVICE_ADDRESS) return PublisherApi(channel=channel) @@ -538,9 +543,12 @@ def make_gax_subscriber_api(connection): :returns: A subscriber API instance with the proper connection configuration. """ - channel = None if connection.in_emulator: channel = insecure_channel(connection.host) + else: + channel = make_secure_channel( + connection.credentials, DEFAULT_USER_AGENT, + SubscriberApi.SERVICE_ADDRESS) return SubscriberApi(channel=channel) diff --git a/pubsub/unit_tests/test__gax.py b/pubsub/unit_tests/test__gax.py index c47b1cd241116..4c2957df14b03 100644 --- a/pubsub/unit_tests/test__gax.py +++ b/pubsub/unit_tests/test__gax.py @@ -879,18 +879,32 @@ def test_live_api(self): from google.cloud.pubsub import _gax as MUT channels = [] + channel_args = [] + channel_obj = object() mock_result = object() + host = 'foo.apis.invalid' def mock_publisher_api(channel): channels.append(channel) return mock_result - connection = _Connection(in_emulator=False) - with _Monkey(MUT, PublisherApi=mock_publisher_api): + def make_channel(*args): + channel_args.append(args) + return channel_obj + + mock_publisher_api.SERVICE_ADDRESS = host + + creds = _Credentials() + connection = _Connection(in_emulator=False, + credentials=creds) + with _Monkey(MUT, PublisherApi=mock_publisher_api, + make_secure_channel=make_channel): result = self._callFUT(connection) self.assertIs(result, mock_result) - self.assertEqual(channels, [None]) + self.assertEqual(channels, [channel_obj]) + self.assertEqual(channel_args, + [(creds, MUT.DEFAULT_USER_AGENT, host)]) def test_emulator(self): from google.cloud._testing import _Monkey @@ -932,18 +946,32 @@ def test_live_api(self): from google.cloud.pubsub import _gax as MUT channels = [] + channel_args = [] + channel_obj = object() mock_result = object() + host = 'foo.apis.invalid' def mock_subscriber_api(channel): channels.append(channel) return mock_result - connection = _Connection(in_emulator=False) - with _Monkey(MUT, SubscriberApi=mock_subscriber_api): + def make_channel(*args): + channel_args.append(args) + return channel_obj + + mock_subscriber_api.SERVICE_ADDRESS = host + + creds = _Credentials() + connection = _Connection(in_emulator=False, + credentials=creds) + with _Monkey(MUT, SubscriberApi=mock_subscriber_api, + make_secure_channel=make_channel): result = self._callFUT(connection) self.assertIs(result, mock_result) - self.assertEqual(channels, [None]) + self.assertEqual(channels, [channel_obj]) + self.assertEqual(channel_args, + [(creds, MUT.DEFAULT_USER_AGENT, host)]) def test_emulator(self): from google.cloud._testing import _Monkey @@ -1143,9 +1171,11 @@ def __init__(self, received_messages): class _Connection(object): - def __init__(self, in_emulator=False, host=None): + def __init__(self, in_emulator=False, host=None, + credentials=None): self.in_emulator = in_emulator self.host = host + self.credentials = credentials class _Client(object): diff --git a/speech/google/cloud/speech/_gax.py b/speech/google/cloud/speech/_gax.py index f75c827674e90..c24f8acd365bb 100644 --- a/speech/google/cloud/speech/_gax.py +++ b/speech/google/cloud/speech/_gax.py @@ -25,6 +25,7 @@ StreamingRecognizeRequest) from google.longrunning import operations_grpc +from google.cloud._helpers import make_secure_channel from google.cloud._helpers import make_secure_stub from google.cloud.connection import DEFAULT_USER_AGENT @@ -38,9 +39,13 @@ class GAPICSpeechAPI(object): """Manage calls through GAPIC wrappers to the Speech API.""" def __init__(self, client=None): self._client = client - self._gapic_api = SpeechApi() + credentials = self._client.connection.credentials + channel = make_secure_channel( + credentials, DEFAULT_USER_AGENT, + SpeechApi.SERVICE_ADDRESS) + self._gapic_api = SpeechApi(channel=channel) self._operations_stub = make_secure_stub( - self._client.connection.credentials, + credentials, DEFAULT_USER_AGENT, operations_grpc.OperationsStub, OPERATIONS_API_HOST) diff --git a/speech/unit_tests/test_client.py b/speech/unit_tests/test_client.py index f8d2455b48f6e..3e02d5ca8d6be 100644 --- a/speech/unit_tests/test_client.py +++ b/speech/unit_tests/test_client.py @@ -226,12 +226,30 @@ def test_sync_recognize_with_empty_results_gax(self): client.connection = _Connection() client.connection.credentials = credentials - def speech_api(): + channels = [] + channel_args = [] + channel_obj = object() + + def make_channel(*args): + channel_args.append(args) + return channel_obj + + def speech_api(channel=None): + channels.append(channel) return _MockGAPICSpeechAPI(response=self._make_sync_response()) - with _Monkey(_gax, SpeechApi=speech_api): + host = 'foo.apis.invalid' + speech_api.SERVICE_ADDRESS = host + + with _Monkey(_gax, SpeechApi=speech_api, + make_secure_channel=make_channel): client._speech_api = _gax.GAPICSpeechAPI(client) + self.assertEqual(channels, [channel_obj]) + self.assertEqual( + channel_args, + [(credentials, _gax.DEFAULT_USER_AGENT, host)]) + sample = Sample(source_uri=self.AUDIO_SOURCE_URI, encoding=speech.Encoding.FLAC, sample_rate=self.SAMPLE_RATE) @@ -259,17 +277,35 @@ def test_sync_recognize_with_gax(self): }] result = self._make_result(alternatives) - def speech_api(): + channels = [] + channel_args = [] + channel_obj = object() + + def make_channel(*args): + channel_args.append(args) + return channel_obj + + def speech_api(channel=None): + channels.append(channel) return _MockGAPICSpeechAPI( response=self._make_sync_response(result)) + host = 'foo.apis.invalid' + speech_api.SERVICE_ADDRESS = host + sample = client.sample(source_uri=self.AUDIO_SOURCE_URI, encoding=speech.Encoding.FLAC, sample_rate=self.SAMPLE_RATE) - with _Monkey(_gax, SpeechApi=speech_api): + with _Monkey(_gax, SpeechApi=speech_api, + make_secure_channel=make_channel): client._speech_api = _gax.GAPICSpeechAPI(client) + self.assertEqual(channels, [channel_obj]) + self.assertEqual( + channel_args, + [(creds, _gax.DEFAULT_USER_AGENT, host)]) + results = client.sync_recognize(sample) self.assertEqual(len(results), 2) @@ -327,16 +363,33 @@ def test_async_recognize_with_gax(self): from google.cloud.speech.operation import Operation credentials = _Credentials() - client = self._makeOne(credentials=credentials) + client = self._makeOne(credentials=credentials, + use_gax=True) client.connection = _Connection() client.connection.credentials = credentials + channel_args = [] + channel_obj = object() + + def make_channel(*args): + channel_args.append(args) + return channel_obj + sample = client.sample(source_uri=self.AUDIO_SOURCE_URI, encoding=speech.Encoding.LINEAR16, sample_rate=self.SAMPLE_RATE) - with _Monkey(_gax, SpeechApi=_MockGAPICSpeechAPI): - operation = client.async_recognize(sample) + with _Monkey(_gax, SpeechApi=_MockGAPICSpeechAPI, + make_secure_channel=make_channel): + api = client.speech_api + + low_level = api._gapic_api + self.assertIsInstance(low_level, _MockGAPICSpeechAPI) + self.assertIs(low_level._channel, channel_obj) + expected = (credentials, _gax.DEFAULT_USER_AGENT, + low_level.SERVICE_ADDRESS) + self.assertEqual(channel_args, [expected]) + operation = client.async_recognize(sample) self.assertIsInstance(operation, Operation) self.assertFalse(operation.complete) self.assertIsNone(operation.response) @@ -350,9 +403,25 @@ def test_speech_api_with_gax(self): creds = _Credentials() client = self._makeOne(credentials=creds, use_gax=True) - with _Monkey(_gax, SpeechApi=_MockGAPICSpeechAPI): - self.assertIsNone(client._speech_api) - self.assertIsInstance(client.speech_api, GAPICSpeechAPI) + channel_args = [] + channel_obj = object() + + def make_channel(*args): + channel_args.append(args) + return channel_obj + + self.assertIsNone(client._speech_api) + with _Monkey(_gax, SpeechApi=_MockGAPICSpeechAPI, + make_secure_channel=make_channel): + api = client.speech_api + + self.assertIsInstance(api, GAPICSpeechAPI) + low_level = api._gapic_api + self.assertIsInstance(low_level, _MockGAPICSpeechAPI) + self.assertIs(low_level._channel, channel_obj) + expected = (creds, _gax.DEFAULT_USER_AGENT, + low_level.SERVICE_ADDRESS) + self.assertEqual(channel_args, [expected]) def test_speech_api_without_gax(self): from google.cloud.connection import Connection @@ -377,9 +446,11 @@ class _MockGAPICSpeechAPI(object): _requests = None _response = None _results = None + SERVICE_ADDRESS = 'foo.apis.invalid' - def __init__(self, response=None): + def __init__(self, response=None, channel=None): self._response = response + self._channel = channel def async_recognize(self, config, audio): from google.longrunning.operations_pb2 import Operation