From 329bfbbbb57c92592fbff219aa2b0bf16fe948a7 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 13 Sep 2021 17:30:11 -0700 Subject: [PATCH 1/6] Add row type --- .../azure/monitor/query/__init__.py | 2 + .../azure/monitor/query/_models.py | 41 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py index d33a6f0a5c28..1171a0562dfc 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/__init__.py @@ -16,6 +16,7 @@ MetricAggregationType, LogsQueryResult, LogsTable, + LogsTableRow, MetricsResult, LogsBatchQuery, MetricNamespace, @@ -38,6 +39,7 @@ "LogsQueryError", "QueryPartialErrorException", "LogsTable", + "LogsTableRow", "LogsBatchQuery", "MetricsQueryClient", "MetricNamespace", diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py index 4532f1ab468e..8532fc309388 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py @@ -48,6 +48,47 @@ def _from_generated(cls, generated): ) +class LogsTableRow(object): + """Represents a single row in logs table. + + ivar list row: The collection of values in the row. + ivar int row_index: The index of the row in the table + """ + def __init__(self, **kwargs): + # type: (Any) -> None + self.row = kwargs.get('row', None) + self.row_index = kwargs.get('row_index', None) + _columns = kwargs.pop('columns', None) + self._row_dict = { + _columns[i]: self.row[i] for i in range(len(self.row)) + } + + @classmethod + def _from_generated(cls, generated): + if not generated: + return None + return cls( + row=generated.row, + row_index=generated.row_index + ) + + def __iter__(self): + """This will iterate over the row directly. + """ + return iter(self.row) + + def __getitem__(self, column): + """This type must be subscriptable directly to row. + Must be gettableby both column name and row index + Example: row[0] -> returns the first element of row and + row[column_name] -> returns the row element against the given column name. + """ + try: + return self._row_dict[column] + except KeyError: + return self.row[column] + + class MetricsResult(object): """The response to a metrics query. From dc5510d08c08d00ea2a38ec0968e33969b872845 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Mon, 13 Sep 2021 18:17:10 -0700 Subject: [PATCH 2/6] Add a new row type --- sdk/monitor/azure-monitor-query/CHANGELOG.md | 1 + .../azure/monitor/query/_models.py | 24 +++++++++---------- .../samples/sample_log_query_client.py | 5 ++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/CHANGELOG.md b/sdk/monitor/azure-monitor-query/CHANGELOG.md index 70c9d0b31bfb..fdd48d8257fc 100644 --- a/sdk/monitor/azure-monitor-query/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-query/CHANGELOG.md @@ -7,6 +7,7 @@ - Added `QueryPartialErrorException` and `LogsQueryError` to handle errors. - Added `partial_error` and `is_error` attributes to `LogsQueryResult`. - Added an option `allow_partial_errors` that defaults to False, which can be set to not throw if there are any partial errors. +- Added a new `LogsTableRow` type that represents a single row in a table. ### Breaking Changes diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py index 8532fc309388..99058016b877 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py @@ -28,7 +28,7 @@ class LogsTable(object): :ivar column_types: The types of columns in this table. :vartype columns: list[object] :ivar rows: Required. The resulting rows from this query. - :vartype rows: list[list[object]] + :vartype rows: list[~azure.monitor.query.LogsTableRow] """ def __init__(self, **kwargs): # type: (Any) -> None @@ -36,7 +36,14 @@ def __init__(self, **kwargs): self.columns = kwargs.pop('columns', None) # type: Optional[str] self.columns_types = kwargs.pop('column_types', None) # type: Optional[Any] _rows = kwargs.pop('rows', None) - self.rows = [process_row(self.columns_types, row) for row in _rows] + self.rows = [ + LogsTableRow( + row=row, + row_index=ind, + col_types=self.columns_types, + columns=self.columns + ) for ind, row in enumerate(_rows) + ] @classmethod def _from_generated(cls, generated): @@ -56,22 +63,15 @@ class LogsTableRow(object): """ def __init__(self, **kwargs): # type: (Any) -> None - self.row = kwargs.get('row', None) + _col_types = kwargs.pop('col_types', None) + row = kwargs.get('row', None) + self.row = process_row(_col_types, row) self.row_index = kwargs.get('row_index', None) _columns = kwargs.pop('columns', None) self._row_dict = { _columns[i]: self.row[i] for i in range(len(self.row)) } - @classmethod - def _from_generated(cls, generated): - if not generated: - return None - return cls( - row=generated.row, - row_index=generated.row_index - ) - def __iter__(self): """This will iterate over the row directly. """ diff --git a/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py b/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py index 5bda7d0e0f3a..d16d38513f65 100644 --- a/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py +++ b/sdk/monitor/azure-monitor-query/samples/sample_log_query_client.py @@ -17,13 +17,14 @@ # Response time trend # request duration over the last 12 hours. # [START send_logs_query] -query = """AppRwequests | take 5""" +query = """AppRequests | take 5""" # returns LogsQueryResult try: response = client.query(os.environ['LOG_WORKSPACE_ID'], query, timespan=timedelta(days=1)) for table in response: - print(table) + df = pd.DataFrame(data=table.rows, columns=table.columns) + print(df) except QueryPartialErrorException as err: print("this is a partial error") print(err.details) From 0fb30aa776440c223675288001d455a4f24d7e66 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 14 Sep 2021 11:20:08 -0700 Subject: [PATCH 3/6] add test --- .../monitor/query/_metrics_query_client.py | 4 ++-- .../tests/async/test_logs_client_async.py | 22 ++++++++++++++++++- .../tests/test_logs_client.py | 21 +++++++++++++++++- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py index 30a99f6f82a0..78f168bb59de 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_metrics_query_client.py @@ -148,8 +148,8 @@ def list_metric_namespaces(self, resource_uri, **kwargs): **kwargs) @distributed_trace - def list_metric_definitions(self, resource_uri, metric_namespace=None, **kwargs): - # type: (str, str, Any) -> ItemPaged[MetricDefinition] + def list_metric_definitions(self, resource_uri, **kwargs): + # type: (str, Any) -> ItemPaged[MetricDefinition] """Lists the metric definitions for the resource. :param resource_uri: The identifier of the resource. diff --git a/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py b/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py index c7675081ed5a..ca99b37ce09a 100644 --- a/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py +++ b/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py @@ -4,7 +4,7 @@ import os from azure.identity.aio import ClientSecretCredential from azure.core.exceptions import HttpResponseError -from azure.monitor.query import LogsBatchQuery, LogsQueryError, LogsTable, LogsQueryResult +from azure.monitor.query import LogsBatchQuery, LogsQueryError, LogsTable, LogsQueryResult, LogsTableRow from azure.monitor.query.aio import LogsQueryClient def _credential(): @@ -202,3 +202,23 @@ async def test_logs_query_result_iterate_over_tables(): assert response.visualization is not None assert len(response.tables) == 2 assert response.__class__ == LogsQueryResult + +@pytest.mark.live_test_only +@pytest.mark.asyncio +async def test_logs_query_result_row_type(): + client = LogsQueryClient(_credential()) + + query = "AppRequests | take 5" + + response = await client.query( + os.environ['LOG_WORKSPACE_ID'], + query, + timespan=None, + ) + + ## should iterate over tables + for table in response: + assert table.__class__ == LogsTable + + row = table.row + assert row.__class__ == LogsTableRow diff --git a/sdk/monitor/azure-monitor-query/tests/test_logs_client.py b/sdk/monitor/azure-monitor-query/tests/test_logs_client.py index 34b00e201dc5..50cac44f5faf 100644 --- a/sdk/monitor/azure-monitor-query/tests/test_logs_client.py +++ b/sdk/monitor/azure-monitor-query/tests/test_logs_client.py @@ -3,7 +3,7 @@ import os from azure.identity import ClientSecretCredential from azure.core.exceptions import HttpResponseError -from azure.monitor.query import LogsQueryClient, LogsBatchQuery, LogsQueryError, LogsTable, LogsQueryResult +from azure.monitor.query import LogsQueryClient, LogsBatchQuery, LogsQueryError, LogsTable, LogsQueryResult, LogsTableRow def _credential(): credential = ClientSecretCredential( @@ -245,3 +245,22 @@ def test_logs_query_result_iterate_over_tables(): assert response.visualization is not None assert len(response.tables) == 2 assert response.__class__ == LogsQueryResult + +@pytest.mark.live_test_only +def test_logs_query_result_row_type(): + client = LogsQueryClient(_credential()) + + query = "AppRequests | take 5" + + response = client.query( + os.environ['LOG_WORKSPACE_ID'], + query, + timespan=None, + ) + + ## should iterate over tables + for table in response: + assert table.__class__ == LogsTable + + row = table.row + assert row.__class__ == LogsTableRow From 4a6c66d6dcc29fec277322ce313c1e38409b68ea Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 14 Sep 2021 11:22:33 -0700 Subject: [PATCH 4/6] lint --- sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py index 99058016b877..c0ab6cc4a0f5 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py @@ -76,7 +76,7 @@ def __iter__(self): """This will iterate over the row directly. """ return iter(self.row) - + def __getitem__(self, column): """This type must be subscriptable directly to row. Must be gettableby both column name and row index From 0a27d3b41ab8c254c70d1bd63578be3e764bcef9 Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 14 Sep 2021 11:43:18 -0700 Subject: [PATCH 5/6] Apply suggestions from code review --- .../azure-monitor-query/tests/async/test_logs_client_async.py | 4 ++-- sdk/monitor/azure-monitor-query/tests/test_logs_client.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py b/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py index ca99b37ce09a..1894d2de3d84 100644 --- a/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py +++ b/sdk/monitor/azure-monitor-query/tests/async/test_logs_client_async.py @@ -220,5 +220,5 @@ async def test_logs_query_result_row_type(): for table in response: assert table.__class__ == LogsTable - row = table.row - assert row.__class__ == LogsTableRow + for row in table.rows: + assert row.__class__ == LogsTableRow diff --git a/sdk/monitor/azure-monitor-query/tests/test_logs_client.py b/sdk/monitor/azure-monitor-query/tests/test_logs_client.py index 50cac44f5faf..a9036055eb0d 100644 --- a/sdk/monitor/azure-monitor-query/tests/test_logs_client.py +++ b/sdk/monitor/azure-monitor-query/tests/test_logs_client.py @@ -262,5 +262,5 @@ def test_logs_query_result_row_type(): for table in response: assert table.__class__ == LogsTable - row = table.row - assert row.__class__ == LogsTableRow + for row in table.rows: + assert row.__class__ == LogsTableRow From 3b5a2ff44ce2c1c15470185088cdbc0149457eea Mon Sep 17 00:00:00 2001 From: Rakshith Bhyravabhotla Date: Tue, 14 Sep 2021 12:47:11 -0700 Subject: [PATCH 6/6] changes --- sdk/monitor/azure-monitor-query/CHANGELOG.md | 1 + .../azure-monitor-query/azure/monitor/query/_models.py | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sdk/monitor/azure-monitor-query/CHANGELOG.md b/sdk/monitor/azure-monitor-query/CHANGELOG.md index fdd48d8257fc..5f2d861f2b22 100644 --- a/sdk/monitor/azure-monitor-query/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-query/CHANGELOG.md @@ -12,6 +12,7 @@ ### Breaking Changes - `LogsQueryResult` now iterates over the tables directly as a convinience. +- `metric_namespace` is renamed to `namespace` and is a keyword-only argument in `list_metric_definitions` API. ### Bugs Fixed diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py index c0ab6cc4a0f5..daa4977803fc 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py @@ -63,11 +63,11 @@ class LogsTableRow(object): """ def __init__(self, **kwargs): # type: (Any) -> None - _col_types = kwargs.pop('col_types', None) - row = kwargs.get('row', None) + _col_types = kwargs['col_types'] + row = kwargs['row'] self.row = process_row(_col_types, row) - self.row_index = kwargs.get('row_index', None) - _columns = kwargs.pop('columns', None) + self.row_index = kwargs['row_index'] + _columns = kwargs['columns'] self._row_dict = { _columns[i]: self.row[i] for i in range(len(self.row)) }