Skip to content

Commit

Permalink
Merge pull request googleapis#2653 from dhermes/logging-iterators-met…
Browse files Browse the repository at this point in the history
…rics

Updating list_metrics() to Iterator pattern.
  • Loading branch information
dhermes authored Nov 1, 2016
2 parents 9d10874 + dd96400 commit ac63bce
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 91 deletions.
2 changes: 1 addition & 1 deletion docs/logging-usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ List all metrics for a project:

>>> from google.cloud import logging
>>> client = logging.Client()
>>> metrics, token = client.list_metrics()
>>> metrics = list(client.list_metrics())
>>> len(metrics)
1
>>> metric = metrics[0]
Expand Down
31 changes: 23 additions & 8 deletions logging/google/cloud/logging/_gax.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from google.cloud.iterator import GAXIterator
from google.cloud.logging._helpers import entry_from_resource
from google.cloud.logging.sink import Sink
from google.cloud.logging.metric import Metric


class _LoggingAPI(object):
Expand Down Expand Up @@ -316,21 +317,18 @@ def list_metrics(self, project, page_size=0, page_token=None):
passed, the API will return the first page of
metrics.
:rtype: tuple, (list, str)
:returns: list of mappings, plus a "next page token" string:
if not None, indicates that more metrics can be retrieved
with another call (pass that value as ``page_token``).
:rtype: :class:`~google.cloud.iterator.Iterator`
:returns: Iterator of
:class:`~google.cloud.logging.metric.Metric`
accessible to the current API.
"""
if page_token is None:
page_token = INITIAL_PAGE
options = CallOptions(page_token=page_token)
path = 'projects/%s' % (project,)
page_iter = self._gax_api.list_log_metrics(
path, page_size=page_size, options=options)
metrics = [MessageToDict(log_metric_pb)
for log_metric_pb in page_iter.next()]
token = page_iter.page_token or None
return metrics, token
return GAXIterator(self._client, page_iter, _item_to_metric)

def metric_create(self, project, metric_name, filter_, description):
"""API call: create a metric resource.
Expand Down Expand Up @@ -496,3 +494,20 @@ def _item_to_sink(iterator, log_sink_pb):
"""
resource = MessageToDict(log_sink_pb)
return Sink.from_api_repr(resource, iterator.client)


def _item_to_metric(iterator, log_metric_pb):
"""Convert a metric protobuf to the native object.
:type iterator: :class:`~google.cloud.iterator.Iterator`
:param iterator: The iterator that is currently in use.
:type log_metric_pb:
:class:`~google.logging.v2.logging_metrics_pb2.LogMetric`
:param log_metric_pb: Metric protobuf returned from the API.
:rtype: :class:`~google.cloud.logging.metric.Metric`
:returns: The next metric in the page.
"""
resource = MessageToDict(log_metric_pb)
return Metric.from_api_repr(resource, iterator.client)
13 changes: 4 additions & 9 deletions logging/google/cloud/logging/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,9 @@ def list_metrics(self, page_size=None, page_token=None):
passed, the API will return the first page of
metrics.
:rtype: tuple, (list, str)
:returns: list of :class:`google.cloud.logging.metric.Metric`, plus a
"next page token" string: if not None, indicates that
more metrics can be retrieved with another call (pass that
value as ``page_token``).
:rtype: :class:`~google.cloud.iterator.Iterator`
:returns: Iterator of :class:`~google.cloud.logging.metric.Metric`
accessible to the current client.
"""
resources, token = self.metrics_api.list_metrics(
return self.metrics_api.list_metrics(
self.project, page_size, page_token)
metrics = [Metric.from_api_repr(resource, self)
for resource in resources]
return metrics, token
39 changes: 26 additions & 13 deletions logging/google/cloud/logging/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from google.cloud.iterator import HTTPIterator
from google.cloud.logging._helpers import entry_from_resource
from google.cloud.logging.sink import Sink
from google.cloud.logging.metric import Metric


class Connection(base_connection.JSONConnection):
Expand Down Expand Up @@ -347,24 +348,21 @@ def list_metrics(self, project, page_size=None, page_token=None):
passed, the API will return the first page of
metrics.
:rtype: tuple, (list, str)
:returns: list of mappings, plus a "next page token" string:
if not None, indicates that more metrics can be retrieved
with another call (pass that value as ``page_token``).
:rtype: :class:`~google.cloud.iterator.Iterator`
:returns: Iterator of
:class:`~google.cloud.logging.metric.Metric`
accessible to the current API.
"""
params = {}
extra_params = {}

if page_size is not None:
params['pageSize'] = page_size

if page_token is not None:
params['pageToken'] = page_token
extra_params['pageSize'] = page_size

path = '/projects/%s/metrics' % (project,)
resp = self._connection.api_request(
method='GET', path=path, query_params=params)
metrics = resp.get('metrics', ())
return metrics, resp.get('nextPageToken')
return HTTPIterator(
client=self._client, path=path,
item_to_value=_item_to_metric, items_key='metrics',
page_token=page_token, extra_params=extra_params)

def metric_create(self, project, metric_name, filter_, description=None):
"""API call: create a metric resource.
Expand Down Expand Up @@ -497,3 +495,18 @@ def _item_to_sink(iterator, resource):
:returns: The next sink in the page.
"""
return Sink.from_api_repr(resource, iterator.client)


def _item_to_metric(iterator, resource):
"""Convert a metric resource to the native object.
:type iterator: :class:`~google.cloud.iterator.Iterator`
:param iterator: The iterator that is currently in use.
:type resource: dict
:param resource: Metric JSON resource returned from the API.
:rtype: :class:`~google.cloud.logging.metric.Metric`
:returns: The next metric in the page.
"""
return Metric.from_api_repr(resource, iterator.client)
52 changes: 34 additions & 18 deletions logging/unit_tests/test__gax.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,56 +843,72 @@ def test_ctor(self):
self.assertIs(api._gax_api, gax_api)

def test_list_metrics_no_paging(self):
import six
from google.gax import INITIAL_PAGE
from google.cloud._testing import _GAXPageIterator
from google.logging.v2.logging_metrics_pb2 import LogMetric
from google.cloud._testing import _GAXPageIterator
from google.cloud.logging.metric import Metric

TOKEN = 'TOKEN'
METRICS = [{
'name': self.METRIC_PATH,
'filter': self.FILTER,
'description': self.DESCRIPTION,
}]
metric_pb = LogMetric(name=self.METRIC_PATH,
description=self.DESCRIPTION,
filter=self.FILTER)
response = _GAXPageIterator([metric_pb], page_token=TOKEN)
gax_api = _GAXMetricsAPI(_list_log_metrics_response=response)
api = self._makeOne(gax_api, None)
client = object()
api = self._makeOne(gax_api, client)

metrics, token = api.list_metrics(self.PROJECT)
iterator = api.list_metrics(self.PROJECT)
page = six.next(iterator.pages)
metrics = list(page)
token = iterator.next_page_token

self.assertEqual(metrics, METRICS)
# First check the token.
self.assertEqual(token, TOKEN)
# Then check the metrics returned.
self.assertEqual(len(metrics), 1)
metric = metrics[0]
self.assertIsInstance(metric, Metric)
self.assertEqual(metric.name, self.METRIC_PATH)
self.assertEqual(metric.filter_, self.FILTER)
self.assertEqual(metric.description, self.DESCRIPTION)
self.assertIs(metric.client, client)

project, page_size, options = gax_api._list_log_metrics_called_with
self.assertEqual(project, self.PROJECT_PATH)
self.assertEqual(page_size, 0)
self.assertEqual(options.page_token, INITIAL_PAGE)

def test_list_metrics_w_paging(self):
from google.cloud._testing import _GAXPageIterator
from google.logging.v2.logging_metrics_pb2 import LogMetric
from google.cloud._testing import _GAXPageIterator
from google.cloud.logging.metric import Metric

TOKEN = 'TOKEN'
PAGE_SIZE = 42
METRICS = [{
'name': self.METRIC_PATH,
'filter': self.FILTER,
'description': self.DESCRIPTION,
}]
metric_pb = LogMetric(name=self.METRIC_PATH,
description=self.DESCRIPTION,
filter=self.FILTER)
response = _GAXPageIterator([metric_pb])
gax_api = _GAXMetricsAPI(_list_log_metrics_response=response)
api = self._makeOne(gax_api, None)
client = object()
api = self._makeOne(gax_api, client)

metrics, token = api.list_metrics(
iterator = api.list_metrics(
self.PROJECT, page_size=PAGE_SIZE, page_token=TOKEN)
metrics = list(iterator)
token = iterator.next_page_token

self.assertEqual(metrics, METRICS)
# First check the token.
self.assertIsNone(token)
# Then check the metrics returned.
self.assertEqual(len(metrics), 1)
metric = metrics[0]
self.assertIsInstance(metric, Metric)
self.assertEqual(metric.name, self.METRIC_PATH)
self.assertEqual(metric.filter_, self.FILTER)
self.assertEqual(metric.description, self.DESCRIPTION)
self.assertIs(metric.client, client)

project, page_size, options = gax_api._list_log_metrics_called_with
self.assertEqual(project, self.PROJECT_PATH)
Expand Down
86 changes: 57 additions & 29 deletions logging/unit_tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,55 +486,90 @@ def test_metric_explicit(self):

def test_list_metrics_no_paging(self):
from google.cloud.logging.metric import Metric
PROJECT = 'PROJECT'
TOKEN = 'TOKEN'
METRICS = [{

metrics = [{
'name': self.METRIC_NAME,
'filter': self.FILTER,
'description': self.DESCRIPTION,
}]
client = self._makeOne(project=PROJECT, credentials=_Credentials())
api = client._metrics_api = _DummyMetricsAPI()
api._list_metrics_response = METRICS, TOKEN
client = self._makeOne(
project=self.PROJECT, credentials=_Credentials(),
use_gax=False)
returned = {
'metrics': metrics,
}
client.connection = _Connection(returned)

metrics, token = client.list_metrics()
# Execute request.
iterator = client.list_metrics()
metrics = list(iterator)

# Check the metrics returned.
self.assertEqual(len(metrics), 1)
metric = metrics[0]
self.assertIsInstance(metric, Metric)
self.assertEqual(metric.name, self.METRIC_NAME)
self.assertEqual(metric.filter_, self.FILTER)
self.assertEqual(metric.description, self.DESCRIPTION)
self.assertEqual(token, TOKEN)
self.assertEqual(api._list_metrics_called_with,
(PROJECT, None, None))
self.assertIs(metric.client, client)

# Verify mocked transport.
called_with = client.connection._called_with
path = '/projects/%s/metrics' % (self.PROJECT,)
self.assertEqual(called_with, {
'method': 'GET',
'path': path,
'query_params': {},
})

def test_list_metrics_with_paging(self):
import six
from google.cloud.logging.metric import Metric
PROJECT = 'PROJECT'
TOKEN = 'TOKEN'
PAGE_SIZE = 42
METRICS = [{

token = 'TOKEN'
next_token = 'T00KEN'
page_size = 42
metrics = [{
'name': self.METRIC_NAME,
'filter': self.FILTER,
'description': self.DESCRIPTION,
}]
client = self._makeOne(project=PROJECT, credentials=_Credentials())
api = client._metrics_api = _DummyMetricsAPI()
api._list_metrics_response = METRICS, None
client = self._makeOne(
project=self.PROJECT, credentials=_Credentials(),
use_gax=False)
returned = {
'metrics': metrics,
'nextPageToken': next_token,
}
client.connection = _Connection(returned)

# Execute request.
metrics, token = client.list_metrics(PAGE_SIZE, TOKEN)
# Test values are correct.
iterator = client.list_metrics(page_size, token)
page = six.next(iterator.pages)
metrics = list(page)

# First check the token.
self.assertEqual(iterator.next_page_token, next_token)
# Then check the metrics returned.
self.assertEqual(len(metrics), 1)
metric = metrics[0]
self.assertIsInstance(metric, Metric)
self.assertEqual(metric.name, self.METRIC_NAME)
self.assertEqual(metric.filter_, self.FILTER)
self.assertEqual(metric.description, self.DESCRIPTION)
self.assertIsNone(token)
self.assertEqual(api._list_metrics_called_with,
(PROJECT, PAGE_SIZE, TOKEN))
self.assertIs(metric.client, client)

# Verify mocked transport.
called_with = client.connection._called_with
path = '/projects/%s/metrics' % (self.PROJECT,)
self.assertEqual(called_with, {
'method': 'GET',
'path': path,
'query_params': {
'pageSize': page_size,
'pageToken': token,
},
})


class _Credentials(object):
Expand All @@ -550,13 +585,6 @@ def create_scoped(self, scope):
return self


class _DummyMetricsAPI(object):

def list_metrics(self, project, page_size, page_token):
self._list_metrics_called_with = (project, page_size, page_token)
return self._list_metrics_response


class _Connection(object):

_called_with = None
Expand Down
Loading

0 comments on commit ac63bce

Please sign in to comment.