diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_helpers.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_helpers.py index 089b74be3091..6f686274361f 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_helpers.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_helpers.py @@ -5,7 +5,7 @@ # license information. # -------------------------------------------------------------------------- -from typing import TYPE_CHECKING +from typing import Mapping, TYPE_CHECKING from azure.core.exceptions import HttpResponseError from azure.core.pipeline.policies import BearerTokenCredentialPolicy @@ -43,3 +43,8 @@ def get_metrics_authentication_policy( def process_error(exception): raise_error = HttpResponseError raise raise_error(message=exception.message, response=exception.response) + +def order_results(request_order, responses): + mapping = {item.id: item for item in responses} + ordered = [mapping[id] for id in request_order] + return ordered diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/_log_query_client.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/_log_query_client.py index e4500b5c543b..2b5352688278 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_log_query_client.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_log_query_client.py @@ -51,10 +51,10 @@ def query(self, workspace_id, query, **kwargs): :param query: The Analytics query. Learn more about the `Analytics query syntax `_. :type query: str - :keyword ~datetime.timedelta timespan: Optional. The timespan over which to query data. This is an ISO8601 time + :keyword str timespan: Optional. The timespan over which to query data. This is an ISO8601 time period value. This timespan is applied in addition to any that are specified in the query expression. - :keyword int server_timeout: the server timeout. The default timeout is 3 minutes, + :keyword int server_timeout: the server timeout in seconds. The default timeout is 3 minutes, and the maximum timeout is 10 minutes. :keyword bool include_statistics: To get information about query statistics. :keyword bool include_render: In the query language, it is possible to specify different render options. @@ -121,9 +121,14 @@ def batch_query(self, queries, **kwargs): queries = [LogsQueryRequest(**q) for q in queries] except (KeyError, TypeError): pass + try: + request_order = [req.id for req in queries] + except AttributeError: + request_order = [req['id'] for req in queries] batch = BatchRequest(requests=queries) + generated = self._query_op.batch(batch, **kwargs) return LogsBatchResults._from_generated( # pylint: disable=protected-access - self._query_op.batch(batch, **kwargs) + generated, request_order ) def close(self): 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 7ae5398f753e..d5a92a6b879f 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 @@ -13,7 +13,7 @@ MonitorQueryClient, ) -from ._models import MetricsResult, MetricDefinition +from ._models import MetricsResult, MetricDefinition, MetricNamespace from ._helpers import get_metrics_authentication_policy if TYPE_CHECKING: @@ -100,7 +100,15 @@ def list_metric_namespaces(self, resource_uri, **kwargs): :rtype: ~azure.core.paging.ItemPaged[~azure.monitor.query.MetricNamespace] :raises: ~azure.core.exceptions.HttpResponseError """ - return self._namespace_op.list(resource_uri, **kwargs) + return self._namespace_op.list( + resource_uri, + cls=kwargs.pop( + "cls", + lambda objs: [ + MetricNamespace._from_generated(x) for x in objs + ] + ), + **kwargs) def list_metric_definitions(self, resource_uri, metric_namespace=None, **kwargs): # type: (str, str, Any) -> ItemPaged[MetricDefinition] @@ -114,7 +122,16 @@ def list_metric_definitions(self, resource_uri, metric_namespace=None, **kwargs) :rtype: ~azure.core.paging.ItemPaged[~azure.monitor.query.MetricDefinition] :raises: ~azure.core.exceptions.HttpResponseError """ - return self._definitions_op.list(resource_uri, metric_namespace, **kwargs) + return self._definitions_op.list( + resource_uri, + metric_namespace, + cls=kwargs.pop( + "cls", + lambda objs: [ + MetricDefinition._from_generated(x) for x in objs + ] + ), + **kwargs) def close(self): # type: () -> None 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 3c66a5766b0f..7673a37149bd 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/_models.py @@ -8,11 +8,11 @@ import uuid from typing import Any, Optional, List +from ._helpers import order_results from ._generated.models import ( Column as InternalColumn, QueryBody as InternalQueryBody, LogQueryRequest as InternalLogQueryRequest, - MetricNamespace as InternalMetricNamespace, ErrorDetails as InternalErrorDetails ) @@ -168,7 +168,7 @@ class LogsQueryRequest(InternalLogQueryRequest): def __init__(self, query, workspace, timespan=None, **kwargs): # type: (str, str, Optional[str], Any) -> None super(LogsQueryRequest, self).__init__(**kwargs) - self.id = kwargs.get("request_id", uuid.uuid4()) + self.id = kwargs.get("request_id", str(uuid.uuid4())) self.headers = kwargs.get("headers", None) self.body = { "query": query, "timespan": timespan @@ -250,13 +250,13 @@ def __init__(self, **kwargs): self.error = kwargs.get("error", None) @classmethod - def _from_generated(cls, generated): + def _from_generated(cls, generated, request_order): if not generated: return cls() return cls( - responses=[ + responses=order_results(request_order, [ LogsQueryResult._from_generated(rsp) for rsp in generated.responses # pylint: disable=protected-access - ], + ]), error=LogsBatchResultError._from_generated(generated.error) # pylint: disable=protected-access ) @@ -314,35 +314,40 @@ def __init__( self.target = kwargs.get('target', None) -class MetricNamespace(InternalMetricNamespace): +class MetricNamespace(object): """Metric namespace class specifies the metadata for a metric namespace. - :param id: The ID of the metricNamespace. - :type id: str - :param type: The type of the namespace. - :type type: str - :param name: The name of the namespace. - :type name: str - :param properties: Properties which include the fully qualified namespace name. - :type properties: ~monitor_query_client.models.MetricNamespaceName + :keyword id: The ID of the metricNamespace. + :paramtype id: str + :keyword type: The type of the namespace. + :paramtype type: str + :keyword name: The name of the namespace. + :paramtype name: str + :keyword metric_namespace_name: The fully qualified namespace name. + :paramtype properties: str """ - - _attribute_map = { - 'id': {'key': 'id', 'type': 'str'}, - 'type': {'key': 'type', 'type': 'str'}, - 'name': {'key': 'name', 'type': 'str'}, - 'properties': {'key': 'properties', 'type': 'MetricNamespaceName'}, - } - def __init__( self, **kwargs ): - super(MetricNamespace, self).__init__(**kwargs) self.id = kwargs.get('id', None) self.type = kwargs.get('type', None) self.name = kwargs.get('name', None) - self.properties = kwargs.get('properties', None) + self.metric_namespace_name = kwargs.get('metric_namespace_name', None) + + @classmethod + def _from_generated(cls, generated): + if not generated: + return cls() + metric_namespace_name = None + if generated.properties: + metric_namespace_name = generated.properties.metric_namespace_name + return cls( + id=generated.id, + type=generated.type, + name=generated.name, + metric_namespace_name=metric_namespace_name + ) class MetricDefinition(object): """Metric definition class specifies the metadata for a metric. @@ -394,6 +399,9 @@ def __init__( def _from_generated(cls, generated): if not generated: return cls() + dimensions = None + if generated.dimensions is not None: + dimensions = [d.value for d in generated.dimensions] return cls( is_dimension_required=generated.is_dimension_required, resource_id=generated.resource_id, @@ -408,7 +416,7 @@ def _from_generated(cls, generated): ) for val in generated.metric_availabilities ], id=generated.id, - dimensions=[d.value for d in generated.dimensions] + dimensions=dimensions ) class MetricValue(object): diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_log_query_client_async.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_log_query_client_async.py index b424565b8cf2..dc5f59c02572 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_log_query_client_async.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_log_query_client_async.py @@ -92,7 +92,7 @@ async def query(self, workspace_id: str, query: str, **kwargs: Any) -> LogsQuery ) try: - return await LogsQueryResults._from_generated(self._query_op.execute( # pylint: disable=protected-access + return LogsQueryResults._from_generated(await self._query_op.execute( # pylint: disable=protected-access workspace_id=workspace_id, body=body, prefer=prefer, @@ -120,9 +120,13 @@ async def batch_query( queries = [LogsQueryRequest(**q) for q in queries] except (KeyError, TypeError): pass + try: + request_order = [req.id for req in queries] + except AttributeError: + request_order = [req['id'] for req in queries] batch = BatchRequest(requests=queries) - return await LogsBatchResults._from_generated( # pylint: disable=protected-access - self._query_op.batch(batch, **kwargs) + return LogsBatchResults._from_generated( # pylint: disable=protected-access + await self._query_op.batch(batch, **kwargs), request_order ) async def __aenter__(self) -> "LogsClient": diff --git a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py index 17579b14a2ef..3cbe043d035e 100644 --- a/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py +++ b/sdk/monitor/azure-monitor-query/azure/monitor/query/aio/_metrics_query_client_async.py @@ -14,7 +14,7 @@ from .._generated.aio._monitor_query_client import ( MonitorQueryClient, ) -from .._models import MetricsResult, MetricDefinition +from .._models import MetricsResult, MetricDefinition, MetricNamespace from .._helpers import get_authentication_policy from .._models import MetricNamespace @@ -95,7 +95,15 @@ async def list_metric_namespaces(self, resource_uri: str, **kwargs: Any) -> Item :rtype: ~azure.core.paging.ItemPaged[~azure.monitor.query.MetricNamespace] :raises: ~azure.core.exceptions.HttpResponseError """ - return await self._namespace_op.list(resource_uri, **kwargs) + return await self._namespace_op.list( + resource_uri, + cls=kwargs.pop( + "cls", + lambda objs: [ + MetricNamespace._from_generated(x) for x in objs + ] + ), + **kwargs) async def list_metric_definitions( self, @@ -113,7 +121,16 @@ async def list_metric_definitions( :rtype: ~azure.core.paging.ItemPaged[~azure.monitor.query.MetricDefinition] :raises: ~azure.core.exceptions.HttpResponseError """ - return await self._definitions_op.list(resource_uri, metric_namespace, **kwargs) + return await self._definitions_op.list( + resource_uri, + metric_namespace, + cls=kwargs.pop( + "cls", + lambda objs: [ + MetricDefinition._from_generated(x) for x in objs + ] + ), + **kwargs) async def __aenter__(self) -> "MetricsClient": await self._client.__aenter__() diff --git a/sdk/monitor/azure-monitor-query/samples/sample_batch_query_build.py b/sdk/monitor/azure-monitor-query/samples/sample_batch_query_build.py index 4189aaaa96b8..955d5c9eed54 100644 --- a/sdk/monitor/azure-monitor-query/samples/sample_batch_query_build.py +++ b/sdk/monitor/azure-monitor-query/samples/sample_batch_query_build.py @@ -36,6 +36,7 @@ for response in response.responses: body = response.body + print(response.id) if not body.tables: print("Something is wrong") else: diff --git a/sdk/monitor/azure-monitor-query/samples/sample_metric_definitions.py b/sdk/monitor/azure-monitor-query/samples/sample_metric_definitions.py index 5edf82628405..d03986a0a4ee 100644 --- a/sdk/monitor/azure-monitor-query/samples/sample_metric_definitions.py +++ b/sdk/monitor/azure-monitor-query/samples/sample_metric_definitions.py @@ -17,4 +17,6 @@ response = client.list_metric_definitions(metrics_uri, metric_namespace='microsoft.eventgrid/topics') for item in response: - pass + print(item) + for availability in item.metric_availabilities: + print(availability.time_grain) diff --git a/sdk/monitor/azure-monitor-query/samples/sample_metric_namespaces.py b/sdk/monitor/azure-monitor-query/samples/sample_metric_namespaces.py new file mode 100644 index 000000000000..7c6bdb0735ab --- /dev/null +++ b/sdk/monitor/azure-monitor-query/samples/sample_metric_namespaces.py @@ -0,0 +1,21 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import os +from azure.monitor.query import MetricsClient +from azure.identity import ClientSecretCredential + +credential = ClientSecretCredential( + client_id = os.environ['AZURE_CLIENT_ID'], + client_secret = os.environ['AZURE_CLIENT_SECRET'], + tenant_id = os.environ['AZURE_TENANT_ID'] + ) + +client = MetricsClient(credential) + +metrics_uri = os.environ['METRICS_RESOURCE_URI'] +response = client.list_metric_namespaces(metrics_uri) + +for item in response: + print(item.metric_namespace_name) + print(item.type) diff --git a/sdk/monitor/azure-monitor-query/sdk_packaging.toml b/sdk/monitor/azure-monitor-query/sdk_packaging.toml index efd98b3f246b..e433bc434275 100644 --- a/sdk/monitor/azure-monitor-query/sdk_packaging.toml +++ b/sdk/monitor/azure-monitor-query/sdk_packaging.toml @@ -1,6 +1,7 @@ [packaging] auto_update = false package_name = "azure-monitor-query" +package_nspkg = "azure-monitor-nspkg" package_pprint_name = "Monitor Query" package_doc_id = "monitor-query" is_stable = false 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 4b587c319544..c34d32620c17 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 @@ -1,8 +1,9 @@ import pytest import os -from azure.identity import ClientSecretCredential +from azure.identity.aio import ClientSecretCredential from azure.core.exceptions import HttpResponseError -from azure.monitor.query import LogsClient, LogsQueryRequest +from azure.monitor.query import LogsQueryRequest +from azure.monitor.query.aio import LogsClient def _credential(): credential = ClientSecretCredential( @@ -12,6 +13,7 @@ def _credential(): ) return credential +@pytest.mark.live_test_only async def test_logs_auth(): credential = _credential() client = LogsClient(credential) @@ -25,6 +27,7 @@ async def test_logs_auth(): assert response is not None assert response.tables is not None +@pytest.mark.live_test_only async def test_logs_server_timeout(): client = LogsClient(_credential()) @@ -36,6 +39,7 @@ async def test_logs_server_timeout(): ) assert e.message.contains('Gateway timeout') +@pytest.mark.live_test_only async def test_logs_batch_query(): client = LogsClient(_credential()) diff --git a/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py b/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py index 192d0d63b682..256994708dea 100644 --- a/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py +++ b/sdk/monitor/azure-monitor-query/tests/async/test_metrics_client_async.py @@ -1,8 +1,8 @@ +import py import pytest import os -from azure.identity import ClientSecretCredential -from azure.core.exceptions import HttpResponseError -from azure.monitor.query import MetricsClient +from azure.identity.aio import ClientSecretCredential +from azure.monitor.query.aio import MetricsClient def _credential(): credential = ClientSecretCredential( @@ -12,6 +12,7 @@ def _credential(): ) return credential +@pytest.mark.live_test_only async def test_metrics_auth(): credential = _credential() client = MetricsClient(credential) @@ -21,6 +22,7 @@ async def test_metrics_auth(): assert response is not None assert response.metrics is not None +@pytest.mark.live_test_only async def test_metrics_namespaces(): client = MetricsClient(_credential()) @@ -28,6 +30,7 @@ async def test_metrics_namespaces(): assert response is not None +@pytest.mark.live_test_only async def test_metrics_definitions(): client = MetricsClient(_credential()) 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 18b55d79942f..e6de47b071f3 100644 --- a/sdk/monitor/azure-monitor-query/tests/test_logs_client.py +++ b/sdk/monitor/azure-monitor-query/tests/test_logs_client.py @@ -12,6 +12,7 @@ def _credential(): ) return credential +@pytest.mark.live_test_only def test_logs_auth(): credential = _credential() client = LogsClient(credential) @@ -25,6 +26,7 @@ def test_logs_auth(): assert response is not None assert response.tables is not None +@pytest.mark.live_test_only def test_logs_server_timeout(): client = LogsClient(_credential()) @@ -36,6 +38,7 @@ def test_logs_server_timeout(): ) assert e.message.contains('Gateway timeout') +@pytest.mark.live_test_only def test_logs_batch_query(): client = LogsClient(_credential()) diff --git a/sdk/monitor/azure-monitor-query/tests/test_metrics_client.py b/sdk/monitor/azure-monitor-query/tests/test_metrics_client.py index ee84c7ab1711..f85d1bec0e22 100644 --- a/sdk/monitor/azure-monitor-query/tests/test_metrics_client.py +++ b/sdk/monitor/azure-monitor-query/tests/test_metrics_client.py @@ -1,7 +1,6 @@ import pytest import os from azure.identity import ClientSecretCredential -from azure.core.exceptions import HttpResponseError from azure.monitor.query import MetricsClient def _credential(): @@ -12,6 +11,7 @@ def _credential(): ) return credential +@pytest.mark.liveTest def test_metrics_auth(): credential = _credential() client = MetricsClient(credential) @@ -21,6 +21,7 @@ def test_metrics_auth(): assert response is not None assert response.metrics is not None +@pytest.mark.liveTest def test_metrics_namespaces(): client = MetricsClient(_credential()) @@ -28,6 +29,7 @@ def test_metrics_namespaces(): assert response is not None +@pytest.mark.liveTest def test_metrics_definitions(): client = MetricsClient(_credential())