Skip to content

Commit 4e1281f

Browse files
authored
Enable advanced query options for batch metrics queries (Azure#36624)
* Enable advanced query options for batch metrics queries
1 parent a2a59ea commit 4e1281f

File tree

7 files changed

+169
-16
lines changed

7 files changed

+169
-16
lines changed

sdk/monitor/azure-monitor-query/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "java",
44
"TagPrefix": "java/monitor/azure-monitor-query",
5-
"Tag": "java/monitor/azure-monitor-query_c61b998a77"
5+
"Tag": "java/monitor/azure-monitor-query_a12f5cac4c"
66
}

sdk/monitor/azure-monitor-query/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,11 @@
8585
<version>1.4.3</version> <!-- {x-version-update;com.azure:azure-core-serializer-json-jackson;dependency} -->
8686
<scope>test</scope>
8787
</dependency>
88+
<dependency>
89+
<groupId>com.azure</groupId>
90+
<artifactId>azure-data-appconfiguration</artifactId>
91+
<version>1.4.8</version> <!-- {x-version-update;com.azure:azure-data-appconfiguration;dependency} -->
92+
<scope>test</scope>
93+
</dependency>
8894
</dependencies>
8995
</project>

sdk/monitor/azure-monitor-query/src/main/java/com/azure/monitor/query/MetricsBatchQueryAsyncClient.java

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414
import com.azure.monitor.query.implementation.metricsbatch.models.MetricResultsResponse;
1515
import com.azure.monitor.query.implementation.metricsbatch.models.MetricResultsResponseValuesItem;
1616
import com.azure.monitor.query.implementation.metricsbatch.models.ResourceIdList;
17+
import com.azure.monitor.query.models.AggregationType;
1718
import com.azure.monitor.query.models.MetricsBatchResult;
19+
import com.azure.monitor.query.models.MetricsQueryOptions;
1820
import com.azure.monitor.query.models.MetricsQueryResult;
1921
import reactor.core.publisher.Mono;
2022

23+
import java.time.Duration;
2124
import java.util.List;
2225
import java.util.Objects;
2326
import java.util.stream.Collectors;
@@ -50,7 +53,7 @@ public final class MetricsBatchQueryAsyncClient {
5053
*/
5154
@ServiceMethod(returns = ReturnType.SINGLE)
5255
public Mono<MetricsBatchResult> queryBatch(List<String> resourceUris, List<String> metricsNames, String metricsNamespace) {
53-
return this.queryBatchWithResponse(resourceUris, metricsNames, metricsNamespace)
56+
return this.queryBatchWithResponse(resourceUris, metricsNames, metricsNamespace, new MetricsQueryOptions())
5457
.map(Response::getValue);
5558
}
5659

@@ -60,13 +63,14 @@ public Mono<MetricsBatchResult> queryBatch(List<String> resourceUris, List<Strin
6063
* @param resourceUris The resource URIs for which the metrics is requested.
6164
* @param metricsNames The names of the metrics to query.
6265
* @param metricsNamespace The namespace of the metrics to query.
66+
* @param options The {@link MetricsQueryOptions} to include for the request.
6367
* @return A time-series metrics result for the requested metric names.
6468
* @throws IllegalArgumentException thrown if {@code resourceUris}, {@code metricsNames} or {@code metricsNamespace} are empty.
6569
* @throws NullPointerException thrown if {@code resourceUris}, {@code metricsNames} or {@code metricsNamespace} are null.
6670
*/
6771
@ServiceMethod(returns = ReturnType.SINGLE)
6872
public Mono<Response<MetricsBatchResult>> queryBatchWithResponse(List<String> resourceUris, List<String> metricsNames,
69-
String metricsNamespace) {
73+
String metricsNamespace, MetricsQueryOptions options) {
7074

7175
if (CoreUtils.isNullOrEmpty(Objects.requireNonNull(resourceUris, "'resourceUris cannot be null."))) {
7276
return monoError(LOGGER, new IllegalArgumentException("resourceUris cannot be empty"));
@@ -80,12 +84,45 @@ public Mono<Response<MetricsBatchResult>> queryBatchWithResponse(List<String> re
8084
return monoError(LOGGER, new IllegalArgumentException("metricsNamespace cannot be empty"));
8185
}
8286

87+
String filter = null;
88+
Duration granularity = null;
89+
String aggregations = null;
90+
String startTime = null;
91+
Integer top = null;
92+
String orderBy = null;
93+
String endTime = null;
94+
if (options != null) {
95+
filter = options.getFilter();
96+
granularity = options.getGranularity();
97+
98+
if (options.getAggregations() != null) {
99+
aggregations = options.getAggregations()
100+
.stream()
101+
.map(AggregationType::toString)
102+
.collect(Collectors.joining(","));
103+
}
104+
if (options.getTimeInterval() != null) {
105+
if (options.getTimeInterval().getDuration() != null) {
106+
return monoError(LOGGER, new IllegalArgumentException("Duration is not a supported time interval for batch query. Use startTime and endTime instead."));
107+
}
108+
if (options.getTimeInterval().getStartTime() != null) {
109+
startTime = options.getTimeInterval().getStartTime().toString();
110+
}
111+
if (options.getTimeInterval().getEndTime() != null) {
112+
endTime = options.getTimeInterval().getEndTime().toString();
113+
}
114+
}
115+
116+
117+
top = options.getTop();
118+
orderBy = options.getOrderBy();
119+
}
83120
String subscriptionId = getSubscriptionFromResourceId(resourceUris.get(0));
84121
ResourceIdList resourceIdList = new ResourceIdList();
85122
resourceIdList.setResourceids(resourceUris);
86123
Mono<Response<MetricResultsResponse>> responseMono = this.serviceClient.getMetrics()
87-
.batchWithResponseAsync(subscriptionId, metricsNamespace, metricsNames, resourceIdList, null,
88-
null, null, null, null, null, null);
124+
.batchWithResponseAsync(subscriptionId, metricsNamespace, metricsNames, resourceIdList, startTime,
125+
endTime, granularity, aggregations, top, orderBy, filter);
89126

90127

91128
return responseMono.map(response -> {

sdk/monitor/azure-monitor-query/src/main/java/com/azure/monitor/query/MetricsBatchQueryClient.java

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
import com.azure.monitor.query.implementation.metricsbatch.models.MetricResultsResponse;
1616
import com.azure.monitor.query.implementation.metricsbatch.models.MetricResultsResponseValuesItem;
1717
import com.azure.monitor.query.implementation.metricsbatch.models.ResourceIdList;
18+
import com.azure.monitor.query.models.AggregationType;
1819
import com.azure.monitor.query.models.MetricsBatchResult;
20+
import com.azure.monitor.query.models.MetricsQueryOptions;
1921
import com.azure.monitor.query.models.MetricsQueryResult;
2022

23+
import java.time.Duration;
2124
import java.util.List;
2225
import java.util.Objects;
2326
import java.util.stream.Collectors;
@@ -49,7 +52,8 @@ public final class MetricsBatchQueryClient {
4952
*/
5053
@ServiceMethod(returns = ReturnType.SINGLE)
5154
public MetricsBatchResult queryBatch(List<String> resourceUris, List<String> metricsNames, String metricsNamespace) {
52-
return this.queryBatchWithResponse(resourceUris, metricsNames, metricsNamespace, Context.NONE).getValue();
55+
return this.queryBatchWithResponse(resourceUris, metricsNames, metricsNamespace, new MetricsQueryOptions(),
56+
Context.NONE).getValue();
5357
}
5458

5559
/**
@@ -58,14 +62,15 @@ public MetricsBatchResult queryBatch(List<String> resourceUris, List<String> met
5862
* @param resourceUris The resource URIs for which the metrics is requested.
5963
* @param metricsNames The names of the metrics to query.
6064
* @param metricsNamespace The namespace of the metrics to query.
65+
* @param options The {@link MetricsQueryOptions} to include for the request.
6166
* @param context The context to associate with this operation.
6267
* @return A time-series metrics result for the requested metric names.
6368
* @throws IllegalArgumentException thrown if {@code resourceUris}, {@code metricsNames} or {@code metricsNamespace} are empty.
6469
* @throws NullPointerException thrown if {@code resourceUris}, {@code metricsNames} or {@code metricsNamespace} are null.
6570
*/
6671
@ServiceMethod(returns = ReturnType.SINGLE)
6772
public Response<MetricsBatchResult> queryBatchWithResponse(List<String> resourceUris, List<String> metricsNames,
68-
String metricsNamespace, Context context) {
73+
String metricsNamespace, MetricsQueryOptions options, Context context) {
6974
if (CoreUtils.isNullOrEmpty(Objects.requireNonNull(resourceUris, "'resourceUris cannot be null."))) {
7075
throw LOGGER.logExceptionAsError(new IllegalArgumentException("resourceUris cannot be empty"));
7176
}
@@ -78,12 +83,45 @@ public Response<MetricsBatchResult> queryBatchWithResponse(List<String> resource
7883
throw LOGGER.logExceptionAsError(new IllegalArgumentException("metricsNamespace cannot be empty"));
7984
}
8085

86+
String filter = null;
87+
Duration granularity = null;
88+
String aggregations = null;
89+
String startTime = null;
90+
Integer top = null;
91+
String orderBy = null;
92+
String endTime = null;
93+
if (options != null) {
94+
filter = options.getFilter();
95+
granularity = options.getGranularity();
96+
97+
if (options.getAggregations() != null) {
98+
aggregations = options.getAggregations()
99+
.stream()
100+
.map(AggregationType::toString)
101+
.collect(Collectors.joining(","));
102+
}
103+
104+
if (options.getTimeInterval() != null) {
105+
if (options.getTimeInterval().getDuration() != null) {
106+
throw LOGGER.logExceptionAsError(new IllegalArgumentException("Duration is not a supported time interval for batch query. Use startTime and endTime instead."));
107+
}
108+
if (options.getTimeInterval().getStartTime() != null) {
109+
startTime = options.getTimeInterval().getStartTime().toString();
110+
}
111+
if (options.getTimeInterval().getEndTime() != null) {
112+
endTime = options.getTimeInterval().getEndTime().toString();
113+
}
114+
}
115+
116+
top = options.getTop();
117+
orderBy = options.getOrderBy();
118+
}
81119
String subscriptionId = getSubscriptionFromResourceId(resourceUris.get(0));
82120
ResourceIdList resourceIdList = new ResourceIdList();
83121
resourceIdList.setResourceids(resourceUris);
84122
Response<MetricResultsResponse> response = this.serviceClient.getMetrics()
85-
.batchWithResponse(subscriptionId, metricsNamespace, metricsNames, resourceIdList, null,
86-
null, null, null, null, null, null, context);
123+
.batchWithResponse(subscriptionId, metricsNamespace, metricsNames, resourceIdList, startTime,
124+
endTime, granularity, aggregations, top, orderBy, filter, context);
87125
MetricResultsResponse value = response.getValue();
88126
List<MetricResultsResponseValuesItem> values = value.getValues();
89127
List<MetricsQueryResult> metricsQueryResults = values.stream()

sdk/monitor/azure-monitor-query/src/test/java/com/azure/monitor/query/MetricsBatchQueryClientTest.java

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,75 @@
33

44
package com.azure.monitor.query;
55

6+
import com.azure.core.exception.HttpResponseException;
67
import com.azure.core.http.policy.HttpLogDetailLevel;
78
import com.azure.core.http.policy.HttpLogOptions;
8-
import com.azure.core.test.annotation.RecordWithoutRequestBody;
99
import com.azure.core.util.Configuration;
10+
import com.azure.core.util.Context;
11+
import com.azure.core.util.CoreUtils;
12+
import com.azure.monitor.query.models.MetricResult;
1013
import com.azure.monitor.query.models.MetricsBatchResult;
14+
import com.azure.monitor.query.models.MetricsQueryOptions;
15+
import com.azure.monitor.query.models.QueryTimeInterval;
1116
import org.junit.jupiter.api.Test;
1217

18+
import java.time.Duration;
19+
import java.time.OffsetDateTime;
1320
import java.util.Arrays;
1421

1522
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
import static org.junit.jupiter.api.Assertions.assertFalse;
24+
import static org.junit.jupiter.api.Assertions.assertThrows;
1625

1726
/**
1827
* Unit tests for {@link MetricsBatchQueryClient}.
1928
*/
2029
public class MetricsBatchQueryClientTest extends MetricsBatchQueryTestBase {
2130

2231
@Test
23-
@RecordWithoutRequestBody
2432
public void testMetricsBatchQuery() {
2533
MetricsBatchQueryClient metricsBatchQueryClient = clientBuilder
2634
.httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS))
2735
.buildClient();
28-
String resourceId = Configuration.getGlobalConfiguration().get("AZURE_MONITOR_METRICS_RESOURCE_URI", FAKE_RESOURCE_ID);
36+
String resourceId = Configuration.getGlobalConfiguration().get("AZURE_MONITOR_METRICS_RESOURCE_URI_2", FAKE_RESOURCE_ID);
2937
resourceId = resourceId.substring(resourceId.indexOf("/subscriptions"));
3038

31-
MetricsBatchResult metricsQueryResults = metricsBatchQueryClient.queryBatch(
32-
Arrays.asList(resourceId),
33-
Arrays.asList("Successful Requests"), " Microsoft.Eventhub/Namespaces");
39+
try {
40+
configClient.getConfigurationSetting("foo", "bar");
41+
} catch (HttpResponseException exception) {
42+
// ignore as this is only to generate some metrics
43+
}
44+
45+
MetricsQueryOptions options = new MetricsQueryOptions()
46+
.setGranularity(Duration.ofMinutes(15))
47+
.setTop(10)
48+
.setTimeInterval(new QueryTimeInterval(OffsetDateTime.now().minusDays(1), OffsetDateTime.now()));
49+
50+
MetricsBatchResult metricsQueryResults = metricsBatchQueryClient.queryBatchWithResponse(
51+
Arrays.asList(resourceId),
52+
Arrays.asList("HttpIncomingRequestCount"), "microsoft.appconfiguration/configurationstores", options, Context.NONE)
53+
.getValue();
3454
assertEquals(1, metricsQueryResults.getMetricsQueryResults().size());
55+
assertEquals(1, metricsQueryResults.getMetricsQueryResults().get(0).getMetrics().size());
56+
MetricResult metricResult = metricsQueryResults.getMetricsQueryResults().get(0).getMetrics().get(0);
57+
assertEquals("HttpIncomingRequestCount", metricResult.getMetricName());
58+
assertFalse(CoreUtils.isNullOrEmpty(metricResult.getTimeSeries()));
3559
}
60+
61+
@Test
62+
public void testMetricsBatchQueryDifferentResourceTypes() {
63+
MetricsBatchQueryClient metricsBatchQueryClient = clientBuilder
64+
.httpLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BODY_AND_HEADERS))
65+
.buildClient();
66+
String resourceId1 = Configuration.getGlobalConfiguration().get("AZURE_MONITOR_METRICS_RESOURCE_URI_1", FAKE_RESOURCE_ID);
67+
String resourceId2 = Configuration.getGlobalConfiguration().get("AZURE_MONITOR_METRICS_RESOURCE_URI_2", FAKE_RESOURCE_ID);
68+
String updatedResource1 = resourceId1.substring(resourceId1.indexOf("/subscriptions"));
69+
String updatedResource2 = resourceId2.substring(resourceId2.indexOf("/subscriptions"));
70+
71+
assertThrows(HttpResponseException.class, () -> metricsBatchQueryClient.queryBatch(
72+
Arrays.asList(updatedResource1, updatedResource2),
73+
Arrays.asList("Successful Requests"), " Microsoft.Eventhub/Namespaces"));
74+
75+
}
76+
3677
}

sdk/monitor/azure-monitor-query/src/test/java/com/azure/monitor/query/MetricsBatchQueryTestBase.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,59 @@
55

66
import com.azure.core.test.TestMode;
77
import com.azure.core.test.TestProxyTestBase;
8+
import com.azure.core.test.models.CustomMatcher;
89
import com.azure.core.test.utils.MockTokenCredential;
910
import com.azure.core.util.Configuration;
11+
import com.azure.data.appconfiguration.ConfigurationClient;
12+
import com.azure.data.appconfiguration.ConfigurationClientBuilder;
1013
import com.azure.identity.DefaultAzureCredentialBuilder;
1114

15+
import java.util.Arrays;
16+
1217
public class MetricsBatchQueryTestBase extends TestProxyTestBase {
1318

1419
static final String FAKE_RESOURCE_ID = "/subscriptions/faa080af-c1d8-40ad-9cce-e1a450ca5b57/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm";
1520
protected String metricEndpoint;
1621
protected MetricsBatchQueryClientBuilder clientBuilder;
22+
protected ConfigurationClient configClient;
1723

1824
@Override
1925
public void beforeTest() {
2026
metricEndpoint = Configuration.getGlobalConfiguration().get("AZURE_MONITOR_METRICS_ENDPOINT", "https://westus.metrics.monitor.azure.com");
2127

2228
MetricsBatchQueryClientBuilder clientBuilder = new MetricsBatchQueryClientBuilder();
29+
ConfigurationClientBuilder configClientBuilder = new ConfigurationClientBuilder();
2330
if (getTestMode() == TestMode.PLAYBACK) {
31+
interceptorManager.addMatchers(new CustomMatcher()
32+
.setIgnoredQueryParameters(Arrays.asList("starttime", "endtime", "api-version"))
33+
.setComparingBodies(false)
34+
.setExcludedHeaders(Arrays.asList("x-ms-content-sha256")));
2435
clientBuilder
2536
.credential(new MockTokenCredential())
2637
.httpClient(interceptorManager.getPlaybackClient());
38+
39+
configClientBuilder
40+
.credential(new MockTokenCredential())
41+
.endpoint("https://fake.azconfig.io")
42+
.httpClient(interceptorManager.getPlaybackClient());
2743
} else if (getTestMode() == TestMode.RECORD) {
44+
interceptorManager.addMatchers(new CustomMatcher()
45+
.setIgnoredQueryParameters(Arrays.asList("starttime", "endtime", "api-version"))
46+
.setComparingBodies(false)
47+
.setExcludedHeaders(Arrays.asList("x-ms-content-sha256")));
2848
clientBuilder
2949
.addPolicy(interceptorManager.getRecordPolicy())
3050
.credential(new DefaultAzureCredentialBuilder().build());
51+
52+
configClientBuilder
53+
.addPolicy(interceptorManager.getRecordPolicy())
54+
.connectionString(Configuration.getGlobalConfiguration().get("AZURE_APPCONFIG_CONNECTION_STRING"));
3155
} else if (getTestMode() == TestMode.LIVE) {
3256
clientBuilder.credential(new DefaultAzureCredentialBuilder().build());
57+
configClientBuilder.connectionString(Configuration.getGlobalConfiguration().get("AZURE_APPCONFIG_CONNECTION_STRING"));
58+
3359
}
3460
this.clientBuilder = clientBuilder.endpoint(metricEndpoint);
61+
this.configClient = configClientBuilder.buildClient();
3562
}
3663
}

sdk/monitor/test-resources.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,13 @@
362362
"type": "string",
363363
"value": "[resourceId('Microsoft.OperationalInsights/workspaces', variables('workspaceName'))]"
364364
},
365-
"AZURE_MONITOR_METRICS_RESOURCE_URI": {
365+
"AZURE_MONITOR_METRICS_RESOURCE_URI_1": {
366366
"type": "string",
367367
"value": "[resourceId('Microsoft.Eventhub/Namespaces', variables('eventHubsNamespaceName'))]"
368+
},
369+
"AZURE_MONITOR_METRICS_RESOURCE_URI_2": {
370+
"type": "string",
371+
"value": "[resourceId('Microsoft.AppConfiguration/configurationStores',variables('uniqueAzConfigName'))]"
368372
}
369373
}
370374
}

0 commit comments

Comments
 (0)