diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RollupClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RollupClient.java index fa36c02d02c..c6b7f1d4234 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RollupClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RollupClient.java @@ -22,6 +22,8 @@ package org.elasticsearch.client; import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.rollup.DeleteRollupJobRequest; import org.elasticsearch.client.rollup.DeleteRollupJobResponse; +import org.elasticsearch.client.rollup.GetRollupIndexCapsRequest; +import org.elasticsearch.client.rollup.GetRollupIndexCapsResponse; import org.elasticsearch.client.rollup.GetRollupJobRequest; import org.elasticsearch.client.rollup.GetRollupJobResponse; import org.elasticsearch.client.rollup.GetRollupCapsRequest; @@ -219,4 +221,40 @@ public class RollupClient { listener, Collections.emptySet()); } + + /** + * Get the Rollup Index Capabilities of a rollup index or pattern + * See + * the docs for more. + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @return the response + * @throws IOException in case there is a problem sending the request or parsing back the response + */ + public GetRollupIndexCapsResponse getRollupIndexCapabilities(GetRollupIndexCapsRequest request, + RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(request, + RollupRequestConverters::getRollupIndexCaps, + options, + GetRollupIndexCapsResponse::fromXContent, + Collections.emptySet()); + } + + /** + * Asynchronously Get the Rollup Index Capabilities of a rollup index or pattern + * See + * the docs for more. + * @param request the request + * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized + * @param listener the listener to be notified upon request completion + */ + public void getRollupIndexCapabilitiesAsync(GetRollupIndexCapsRequest request, RequestOptions options, + ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(request, + RollupRequestConverters::getRollupIndexCaps, + options, + GetRollupIndexCapsResponse::fromXContent, + listener, + Collections.emptySet()); + } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RollupRequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RollupRequestConverters.java index f662d592d1a..f0fe801ae59 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RollupRequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RollupRequestConverters.java @@ -24,6 +24,7 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.elasticsearch.client.rollup.DeleteRollupJobRequest; import org.elasticsearch.client.rollup.GetRollupCapsRequest; +import org.elasticsearch.client.rollup.GetRollupIndexCapsRequest; import org.elasticsearch.client.rollup.GetRollupJobRequest; import org.elasticsearch.client.rollup.PutRollupJobRequest; import org.elasticsearch.client.rollup.StartRollupJobRequest; @@ -85,4 +86,14 @@ final class RollupRequestConverters { request.setEntity(createEntity(getRollupCapsRequest, REQUEST_BODY_CONTENT_TYPE)); return request; } + + static Request getRollupIndexCaps(final GetRollupIndexCapsRequest getRollupIndexCapsRequest) throws IOException { + String endpoint = new RequestConverters.EndpointBuilder() + .addCommaSeparatedPathParts(getRollupIndexCapsRequest.indices()) + .addPathPartAsIs("_xpack", "rollup", "data") + .build(); + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + request.setEntity(createEntity(getRollupIndexCapsRequest, REQUEST_BODY_CONTENT_TYPE)); + return request; + } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/rollup/GetRollupCapsResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/rollup/GetRollupCapsResponse.java index 872dc7440a0..f4c516d015d 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/rollup/GetRollupCapsResponse.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/rollup/GetRollupCapsResponse.java @@ -18,10 +18,6 @@ */ package org.elasticsearch.client.rollup; -import org.elasticsearch.common.Strings; -import org.elasticsearch.common.xcontent.ToXContent; -import org.elasticsearch.common.xcontent.ToXContentObject; -import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; @@ -30,7 +26,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Objects; -public class GetRollupCapsResponse implements ToXContentObject { +public class GetRollupCapsResponse { private final Map jobs; @@ -42,16 +38,6 @@ public class GetRollupCapsResponse implements ToXContentObject { return jobs; } - @Override - public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { - builder.startObject(); - for (Map.Entry entry : jobs.entrySet()) { - entry.getValue().toXContent(builder, params); - } - builder.endObject(); - return builder; - } - public static GetRollupCapsResponse fromXContent(final XContentParser parser) throws IOException { Map jobs = new HashMap<>(); XContentParser.Token token = parser.nextToken(); @@ -84,9 +70,4 @@ public class GetRollupCapsResponse implements ToXContentObject { GetRollupCapsResponse other = (GetRollupCapsResponse) obj; return Objects.equals(jobs, other.jobs); } - - @Override - public final String toString() { - return Strings.toString(this); - } } diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/rollup/GetRollupIndexCapsRequest.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/rollup/GetRollupIndexCapsRequest.java new file mode 100644 index 00000000000..6bb0c9b48d6 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/rollup/GetRollupIndexCapsRequest.java @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.rollup; + +import org.elasticsearch.action.support.IndicesOptions; +import org.elasticsearch.client.Validatable; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.xcontent.ToXContentObject; +import org.elasticsearch.common.xcontent.XContentBuilder; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Objects; + +public class GetRollupIndexCapsRequest implements Validatable, ToXContentObject { + private static final String INDICES = "indices"; + private static final String INDICES_OPTIONS = "indices_options"; + + private String[] indices; + private IndicesOptions options; + + public GetRollupIndexCapsRequest(final String... indices) { + this(indices, IndicesOptions.STRICT_EXPAND_OPEN_FORBID_CLOSED); + } + + public GetRollupIndexCapsRequest(final String[] indices, final IndicesOptions options) { + if (indices == null || indices.length == 0) { + throw new IllegalArgumentException("[indices] must not be null or empty"); + } + for (String index : indices) { + if (Strings.isNullOrEmpty(index)) { + throw new IllegalArgumentException("[index] must not be null or empty"); + } + } + this.indices = indices; + this.options = Objects.requireNonNull(options); + } + + public IndicesOptions indicesOptions() { + return options; + } + + public String[] indices() { + return indices; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + { + builder.array(INDICES, indices); + builder.startObject(INDICES_OPTIONS); + { + options.toXContent(builder, params); + } + builder.endObject(); + } + builder.endObject(); + return builder; + } + + @Override + public int hashCode() { + return Objects.hash(Arrays.hashCode(indices), options); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + GetRollupIndexCapsRequest other = (GetRollupIndexCapsRequest) obj; + return Arrays.equals(indices, other.indices) + && Objects.equals(options, other.options); + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/rollup/GetRollupIndexCapsResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/rollup/GetRollupIndexCapsResponse.java new file mode 100644 index 00000000000..831e10f1747 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/rollup/GetRollupIndexCapsResponse.java @@ -0,0 +1,73 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.rollup; + +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class GetRollupIndexCapsResponse { + + private final Map jobs; + + public GetRollupIndexCapsResponse(final Map jobs) { + this.jobs = Collections.unmodifiableMap(Objects.requireNonNull(jobs)); + } + + public Map getJobs() { + return jobs; + } + + public static GetRollupIndexCapsResponse fromXContent(final XContentParser parser) throws IOException { + Map jobs = new HashMap<>(); + XContentParser.Token token = parser.nextToken(); + if (token.equals(XContentParser.Token.START_OBJECT)) { + while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) { + if (token.equals(XContentParser.Token.FIELD_NAME)) { + String pattern = parser.currentName(); + + RollableIndexCaps cap = RollableIndexCaps.PARSER.apply(pattern).apply(parser, null); + jobs.put(pattern, cap); + } + } + } + return new GetRollupIndexCapsResponse(jobs); + } + + @Override + public int hashCode() { + return Objects.hash(jobs); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + GetRollupIndexCapsResponse other = (GetRollupIndexCapsResponse) obj; + return Objects.equals(jobs, other.jobs); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RollupIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RollupIT.java index ffe75bfc1b1..213ca8710a4 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RollupIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RollupIT.java @@ -33,6 +33,8 @@ import org.elasticsearch.client.rollup.DeleteRollupJobRequest; import org.elasticsearch.client.rollup.DeleteRollupJobResponse; import org.elasticsearch.client.rollup.GetRollupCapsRequest; import org.elasticsearch.client.rollup.GetRollupCapsResponse; +import org.elasticsearch.client.rollup.GetRollupIndexCapsRequest; +import org.elasticsearch.client.rollup.GetRollupIndexCapsResponse; import org.elasticsearch.client.rollup.GetRollupJobRequest; import org.elasticsearch.client.rollup.GetRollupJobResponse; import org.elasticsearch.client.rollup.GetRollupJobResponse.IndexerState; @@ -348,4 +350,116 @@ public class RollupIT extends ESRestHighLevelClientTestCase { List> valueCaps = fieldCaps.get("value").getAggs(); assertThat(valueCaps.size(), equalTo(SUPPORTED_METRICS.size())); } + + public void testGetRollupIndexCaps() throws Exception { + final Set values = new HashSet<>(); + double sum = 0.0d; + int max = Integer.MIN_VALUE; + int min = Integer.MAX_VALUE; + + final BulkRequest bulkRequest = new BulkRequest(); + bulkRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + for (int minute = 0; minute < 60; minute++) { + for (int second = 0; second < 60; second = second + 10) { + final int value = randomIntBetween(0, 100); + + final IndexRequest indexRequest = new IndexRequest("docs", "doc"); + indexRequest.source(jsonBuilder() + .startObject() + .field("value", value) + .field("date", String.format(Locale.ROOT, "2018-01-01T00:%02d:%02dZ", minute, second)) + .endObject()); + bulkRequest.add(indexRequest); + + values.add(value); + sum += value; + if (value > max) { + max = value; + } + if (value < min) { + min = value; + } + } + } + + final int numDocs = bulkRequest.numberOfActions(); + + BulkResponse bulkResponse = highLevelClient().bulk(bulkRequest, RequestOptions.DEFAULT); + assertEquals(RestStatus.OK, bulkResponse.status()); + if (bulkResponse.hasFailures()) { + for (BulkItemResponse itemResponse : bulkResponse.getItems()) { + if (itemResponse.isFailed()) { + logger.fatal(itemResponse.getFailureMessage()); + } + } + } + assertFalse(bulkResponse.hasFailures()); + + RefreshResponse refreshResponse = highLevelClient().indices().refresh(new RefreshRequest("docs"), RequestOptions.DEFAULT); + assertEquals(0, refreshResponse.getFailedShards()); + + final String id = randomAlphaOfLength(10); + final String indexPattern = randomFrom("docs", "d*", "doc*"); + final String rollupIndex = randomFrom("rollup", "test"); + final String cron = "*/1 * * * * ?"; + final int pageSize = randomIntBetween(numDocs, numDocs * 10); + // TODO expand this to also test with histogram and terms? + final GroupConfig groups = new GroupConfig(new DateHistogramGroupConfig("date", DateHistogramInterval.DAY)); + final List metrics = Collections.singletonList(new MetricConfig("value", SUPPORTED_METRICS)); + final TimeValue timeout = TimeValue.timeValueSeconds(randomIntBetween(30, 600)); + + PutRollupJobRequest putRollupJobRequest = + new PutRollupJobRequest(new RollupJobConfig(id, indexPattern, rollupIndex, cron, pageSize, groups, metrics, timeout)); + + final RollupClient rollupClient = highLevelClient().rollup(); + PutRollupJobResponse response = execute(putRollupJobRequest, rollupClient::putRollupJob, rollupClient::putRollupJobAsync); + assertTrue(response.isAcknowledged()); + + // wait for the PutJob api to create the index w/ metadata + highLevelClient().cluster().health(new ClusterHealthRequest(rollupIndex).waitForYellowStatus(), RequestOptions.DEFAULT); + + GetRollupIndexCapsRequest getRollupIndexCapsRequest = new GetRollupIndexCapsRequest(rollupIndex); + GetRollupIndexCapsResponse capsResponse = highLevelClient().rollup() + .getRollupIndexCapabilities(getRollupIndexCapsRequest, RequestOptions.DEFAULT); + + assertNotNull(capsResponse); + Map rolledPatterns = capsResponse.getJobs(); + assertThat(rolledPatterns.size(), equalTo(1)); + + RollableIndexCaps docsPattern = rolledPatterns.get(rollupIndex); + assertThat(docsPattern.getIndexName(), equalTo(rollupIndex)); + + List rollupJobs = docsPattern.getJobCaps(); + assertThat(rollupJobs.size(), equalTo(1)); + + RollupJobCaps jobCaps = rollupJobs.get(0); + assertThat(jobCaps.getJobID(), equalTo(id)); + assertThat(jobCaps.getRollupIndex(), equalTo(rollupIndex)); + assertThat(jobCaps.getIndexPattern(), equalTo(indexPattern)); + + Map fieldCaps = jobCaps.getFieldCaps(); + + List> timestampCaps = fieldCaps.get("date").getAggs(); + for (Map.Entry entry : timestampCaps.get(0).entrySet()) { + switch (entry.getKey()) { + case "agg": + assertThat(entry.getValue(), equalTo("date_histogram")); + break; + case "delay": + assertThat(entry.getValue(), equalTo("foo")); + break; + case "interval": + assertThat(entry.getValue(), equalTo("1d")); + break; + case "time_zone": + assertThat(entry.getValue(), equalTo("UTC")); + break; + default: + fail("Unknown field cap: [" + entry.getKey() + "]"); + } + } + + List> valueCaps = fieldCaps.get("value").getAggs(); + assertThat(valueCaps.size(), equalTo(SUPPORTED_METRICS.size())); + } } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/GetRollupCapsResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/GetRollupCapsResponseTests.java index af595b210e9..a728b65cf64 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/GetRollupCapsResponseTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/GetRollupCapsResponseTests.java @@ -18,52 +18,13 @@ */ package org.elasticsearch.client.rollup; -import org.elasticsearch.client.rollup.job.config.DateHistogramGroupConfig; -import org.elasticsearch.client.rollup.job.config.GroupConfig; -import org.elasticsearch.client.rollup.job.config.HistogramGroupConfig; -import org.elasticsearch.client.rollup.job.config.MetricConfig; -import org.elasticsearch.client.rollup.job.config.RollupJobConfig; -import org.elasticsearch.client.rollup.job.config.RollupJobConfigTests; -import org.elasticsearch.client.rollup.job.config.TermsGroupConfig; +import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentParser; -import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; -import org.elasticsearch.test.AbstractXContentTestCase; -import org.junit.Before; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.stream.Collectors; -import static java.util.Collections.singletonMap; - -public class GetRollupCapsResponseTests extends AbstractXContentTestCase { - - private Map indices; - - @Before - private void setupIndices() throws IOException { - int numIndices = randomIntBetween(1,5); - indices = new HashMap<>(numIndices); - for (int i = 0; i < numIndices; i++) { - String indexName = "index_" + randomAlphaOfLength(10); - int numJobs = randomIntBetween(1,5); - List jobs = new ArrayList<>(numJobs); - for (int j = 0; j < numJobs; j++) { - RollupJobConfig config = RollupJobConfigTests.randomRollupJobConfig(randomAlphaOfLength(10)); - jobs.add(new RollupJobCaps(config.getId(), config.getIndexPattern(), - config.getRollupIndex(), createRollupFieldCaps(config))); - } - RollableIndexCaps cap = new RollableIndexCaps(indexName, jobs); - indices.put(indexName, cap); - } - } +public class GetRollupCapsResponseTests extends RollupCapsResponseTestCase { @Override protected GetRollupCapsResponse createTestInstance() { @@ -71,82 +32,16 @@ public class GetRollupCapsResponseTests extends AbstractXContentTestCase entry : response.getJobs().entrySet()) { + entry.getValue().toXContent(builder, null); + } + builder.endObject(); } @Override - protected GetRollupCapsResponse doParseInstance(final XContentParser parser) throws IOException { + protected GetRollupCapsResponse fromXContent(XContentParser parser) throws IOException { return GetRollupCapsResponse.fromXContent(parser); } - - /** - * Lifted from core's RollupJobCaps, so that we can test without having to include this actual logic in the request - */ - private static Map createRollupFieldCaps(final RollupJobConfig rollupJobConfig) { - final Map>> tempFieldCaps = new HashMap<>(); - - final GroupConfig groupConfig = rollupJobConfig.getGroupConfig(); - if (groupConfig != null) { - // Create RollupFieldCaps for the date histogram - final DateHistogramGroupConfig dateHistogram = groupConfig.getDateHistogram(); - final Map dateHistogramAggCap = new HashMap<>(); - dateHistogramAggCap.put("agg", DateHistogramAggregationBuilder.NAME); - dateHistogramAggCap.put("interval", dateHistogram.getInterval().toString()); - if (dateHistogram.getDelay() != null) { - dateHistogramAggCap.put("delay", dateHistogram.getDelay().toString()); - } - dateHistogramAggCap.put("time_zone", dateHistogram.getTimeZone()); - - List> dateAggCaps = tempFieldCaps.getOrDefault(dateHistogram.getField(), new ArrayList<>()); - dateAggCaps.add(dateHistogramAggCap); - tempFieldCaps.put(dateHistogram.getField(), dateAggCaps); - - // Create RollupFieldCaps for the histogram - final HistogramGroupConfig histogram = groupConfig.getHistogram(); - if (histogram != null) { - final Map histogramAggCap = new HashMap<>(); - histogramAggCap.put("agg", HistogramAggregationBuilder.NAME); - histogramAggCap.put("interval", histogram.getInterval()); - Arrays.stream(rollupJobConfig.getGroupConfig().getHistogram().getFields()).forEach(field -> { - List> caps = tempFieldCaps.getOrDefault(field, new ArrayList<>()); - caps.add(histogramAggCap); - tempFieldCaps.put(field, caps); - }); - } - - // Create RollupFieldCaps for the term - final TermsGroupConfig terms = groupConfig.getTerms(); - if (terms != null) { - final Map termsAggCap = singletonMap("agg", TermsAggregationBuilder.NAME); - Arrays.stream(rollupJobConfig.getGroupConfig().getTerms().getFields()).forEach(field -> { - List> caps = tempFieldCaps.getOrDefault(field, new ArrayList<>()); - caps.add(termsAggCap); - tempFieldCaps.put(field, caps); - }); - } - } - - // Create RollupFieldCaps for the metrics - final List metricsConfig = rollupJobConfig.getMetricsConfig(); - if (metricsConfig.size() > 0) { - rollupJobConfig.getMetricsConfig().forEach(metricConfig -> { - final List> metrics = metricConfig.getMetrics().stream() - .map(metric -> singletonMap("agg", (Object) metric)) - .collect(Collectors.toList()); - metrics.forEach(m -> { - List> caps = tempFieldCaps - .getOrDefault(metricConfig.getField(), new ArrayList<>()); - caps.add(m); - tempFieldCaps.put(metricConfig.getField(), caps); - }); - }); - } - - return Collections.unmodifiableMap(tempFieldCaps.entrySet() - .stream() - .collect(Collectors.toMap(Map.Entry::getKey, - e -> new RollupJobCaps.RollupFieldCaps(e.getValue())))); - } - } diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/GetRollupIndexCapsRequestTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/GetRollupIndexCapsRequestTests.java new file mode 100644 index 00000000000..f53b18a977d --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/GetRollupIndexCapsRequestTests.java @@ -0,0 +1,38 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.rollup; + +import org.elasticsearch.test.ESTestCase; + +import static org.hamcrest.Matchers.equalTo; + +public class GetRollupIndexCapsRequestTests extends ESTestCase { + + public void testNullOrEmptyIndices() { + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> new GetRollupIndexCapsRequest((String[]) null)); + assertThat(e.getMessage(), equalTo("[indices] must not be null or empty")); + + String[] indices = new String[]{}; + e = expectThrows(IllegalArgumentException.class, () -> new GetRollupIndexCapsRequest(indices)); + assertThat(e.getMessage(), equalTo("[indices] must not be null or empty")); + + e = expectThrows(IllegalArgumentException.class, () -> new GetRollupIndexCapsRequest(new String[]{"foo", null})); + assertThat(e.getMessage(), equalTo("[index] must not be null or empty")); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/GetRollupIndexCapsResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/GetRollupIndexCapsResponseTests.java new file mode 100644 index 00000000000..afd0e54f92b --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/GetRollupIndexCapsResponseTests.java @@ -0,0 +1,47 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.rollup; + +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; + +import java.io.IOException; +import java.util.Map; + +public class GetRollupIndexCapsResponseTests extends RollupCapsResponseTestCase { + + @Override + protected GetRollupIndexCapsResponse createTestInstance() { + return new GetRollupIndexCapsResponse(indices); + } + + @Override + protected void toXContent(GetRollupIndexCapsResponse response, XContentBuilder builder) throws IOException { + builder.startObject(); + for (Map.Entry entry : response.getJobs().entrySet()) { + entry.getValue().toXContent(builder, null); + } + builder.endObject(); + } + + @Override + protected GetRollupIndexCapsResponse fromXContent(XContentParser parser) throws IOException { + return GetRollupIndexCapsResponse.fromXContent(parser); + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/RollupCapsResponseTestCase.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/RollupCapsResponseTestCase.java new file mode 100644 index 00000000000..6d1c0359d17 --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/rollup/RollupCapsResponseTestCase.java @@ -0,0 +1,156 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.client.rollup; + +import org.elasticsearch.client.rollup.job.config.DateHistogramGroupConfig; +import org.elasticsearch.client.rollup.job.config.GroupConfig; +import org.elasticsearch.client.rollup.job.config.HistogramGroupConfig; +import org.elasticsearch.client.rollup.job.config.MetricConfig; +import org.elasticsearch.client.rollup.job.config.RollupJobConfig; +import org.elasticsearch.client.rollup.job.config.RollupJobConfigTests; +import org.elasticsearch.client.rollup.job.config.TermsGroupConfig; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.histogram.HistogramAggregationBuilder; +import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; +import org.elasticsearch.test.ESTestCase; +import org.junit.Before; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static java.util.Collections.singletonMap; +import static org.elasticsearch.test.AbstractXContentTestCase.xContentTester; + +abstract class RollupCapsResponseTestCase extends ESTestCase { + + protected Map indices; + + protected abstract T createTestInstance(); + + protected abstract void toXContent(T response, XContentBuilder builder) throws IOException; + + protected abstract T fromXContent(XContentParser parser) throws IOException; + + public void testFromXContent() throws IOException { + xContentTester( + this::createParser, + this::createTestInstance, + this::toXContent, + this::fromXContent) + .supportsUnknownFields(false) + .randomFieldsExcludeFilter(field -> + field.endsWith("job_id")) + .test(); + } + + @Before + private void setupIndices() throws IOException { + int numIndices = randomIntBetween(1,5); + indices = new HashMap<>(numIndices); + for (int i = 0; i < numIndices; i++) { + String indexName = "index_" + randomAlphaOfLength(10); + int numJobs = randomIntBetween(1,5); + List jobs = new ArrayList<>(numJobs); + for (int j = 0; j < numJobs; j++) { + RollupJobConfig config = RollupJobConfigTests.randomRollupJobConfig(randomAlphaOfLength(10)); + jobs.add(new RollupJobCaps(config.getId(), config.getIndexPattern(), + config.getRollupIndex(), createRollupFieldCaps(config))); + } + RollableIndexCaps cap = new RollableIndexCaps(indexName, jobs); + indices.put(indexName, cap); + } + } + + /** + * Lifted from core's RollupJobCaps, so that we can test without having to include this actual logic in the request + */ + private static Map createRollupFieldCaps(final RollupJobConfig rollupJobConfig) { + final Map>> tempFieldCaps = new HashMap<>(); + + final GroupConfig groupConfig = rollupJobConfig.getGroupConfig(); + if (groupConfig != null) { + // Create RollupFieldCaps for the date histogram + final DateHistogramGroupConfig dateHistogram = groupConfig.getDateHistogram(); + final Map dateHistogramAggCap = new HashMap<>(); + dateHistogramAggCap.put("agg", DateHistogramAggregationBuilder.NAME); + dateHistogramAggCap.put("interval", dateHistogram.getInterval().toString()); + if (dateHistogram.getDelay() != null) { + dateHistogramAggCap.put("delay", dateHistogram.getDelay().toString()); + } + dateHistogramAggCap.put("time_zone", dateHistogram.getTimeZone()); + + List> dateAggCaps = tempFieldCaps.getOrDefault(dateHistogram.getField(), new ArrayList<>()); + dateAggCaps.add(dateHistogramAggCap); + tempFieldCaps.put(dateHistogram.getField(), dateAggCaps); + + // Create RollupFieldCaps for the histogram + final HistogramGroupConfig histogram = groupConfig.getHistogram(); + if (histogram != null) { + final Map histogramAggCap = new HashMap<>(); + histogramAggCap.put("agg", HistogramAggregationBuilder.NAME); + histogramAggCap.put("interval", histogram.getInterval()); + Arrays.stream(rollupJobConfig.getGroupConfig().getHistogram().getFields()).forEach(field -> { + List> caps = tempFieldCaps.getOrDefault(field, new ArrayList<>()); + caps.add(histogramAggCap); + tempFieldCaps.put(field, caps); + }); + } + + // Create RollupFieldCaps for the term + final TermsGroupConfig terms = groupConfig.getTerms(); + if (terms != null) { + final Map termsAggCap = singletonMap("agg", TermsAggregationBuilder.NAME); + Arrays.stream(rollupJobConfig.getGroupConfig().getTerms().getFields()).forEach(field -> { + List> caps = tempFieldCaps.getOrDefault(field, new ArrayList<>()); + caps.add(termsAggCap); + tempFieldCaps.put(field, caps); + }); + } + } + + // Create RollupFieldCaps for the metrics + final List metricsConfig = rollupJobConfig.getMetricsConfig(); + if (metricsConfig.size() > 0) { + rollupJobConfig.getMetricsConfig().forEach(metricConfig -> { + final List> metrics = metricConfig.getMetrics().stream() + .map(metric -> singletonMap("agg", (Object) metric)) + .collect(Collectors.toList()); + metrics.forEach(m -> { + List> caps = tempFieldCaps + .getOrDefault(metricConfig.getField(), new ArrayList<>()); + caps.add(m); + tempFieldCaps.put(metricConfig.getField(), caps); + }); + }); + } + + return Collections.unmodifiableMap(tempFieldCaps.entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, + e -> new RollupJobCaps.RollupFieldCaps(e.getValue())))); + } +} diff --git a/docs/java-rest/high-level/rollup/get_rollup_index_caps.asciidoc b/docs/java-rest/high-level/rollup/get_rollup_index_caps.asciidoc new file mode 100644 index 00000000000..52cb7ff9524 --- /dev/null +++ b/docs/java-rest/high-level/rollup/get_rollup_index_caps.asciidoc @@ -0,0 +1,84 @@ +-- +:api: rollup-get-rollup-index-caps +:request: GetRollupIndexCapsRequest +:response: GetRollupIndexCapsResponse +-- + +[id="{upid}-x-pack-{api}"] +=== Get Rollup Index Capabilities API + +The Get Rollup Index Capabilities API allows the user to determine if a concrete index or index pattern contains +stored rollup jobs and data. If it contains data stored from rollup jobs, the capabilities of those jobs +are returned. The API accepts a `GetRollupIndexCapsRequest` object as a request and returns a `GetRollupIndexCapsResponse`. + +[id="{upid}-x-pack-{api}-request"] +==== Get Rollup Index Capabilities Request + +A +{request}+ requires a single parameter: the target index or index pattern (e.g. `rollup-foo`): + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[x-pack-{api}-request] +-------------------------------------------------- + +[id="{upid}-x-pack-{api}-execution"] +==== Execution + +The Get Rollup Index Capabilities API can be executed through a `RollupClient` +instance. Such instance can be retrieved from a `RestHighLevelClient` +using the `rollup()` method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[x-pack-{api}-execute] +-------------------------------------------------- + +[id="{upid}-x-pack-{api}-response"] +==== Response + +The returned +{response}+ holds lists and maps of values which correspond to the capabilities +of the rollup index/index pattern (what jobs are stored in the index, their capabilities, what +aggregations are available, etc). Because multiple jobs can be stored in one index, the +response may include several jobs with different configurations. + +The capabilities are essentially the same as the original job configuration, just presented in a different +manner. For example, if we had created a job with the following config: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[x-pack-{api}-setup] +-------------------------------------------------- + +The +{response}+ object would contain the same information, laid out in a slightly different manner: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[x-pack-{api}-response] +-------------------------------------------------- + +[id="{upid}-x-pack-{api}-async"] +==== Asynchronous Execution + +This request can be executed asynchronously: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[x-pack-{api}-execute-async] +-------------------------------------------------- +<1> The +{request}+ to execute and the `ActionListener` to use when +the execution completes + +The asynchronous method does not block and returns immediately. Once it is +completed the `ActionListener` is called back using the `onResponse` method +if the execution successfully completed or using the `onFailure` method if +it failed. + +A typical listener for +{response}+ looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests-file}[x-pack-{api}-execute-listener] +-------------------------------------------------- +<1> Called when the execution is successfully completed. The response is +provided as an argument +<2> Called in case of failure. The raised exception is provided as an argument diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index dd867d4691a..1589a476c29 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -311,12 +311,14 @@ The Java High Level REST Client supports the following Rollup APIs: * <<{upid}-rollup-delete-job>> * <> * <<{upid}-x-pack-rollup-get-rollup-caps>> +* <<{upid}-x-pack-rollup-get-rollup-index-caps>> include::rollup/put_job.asciidoc[] include::rollup/start_job.asciidoc[] include::rollup/delete_job.asciidoc[] include::rollup/get_job.asciidoc[] include::rollup/get_rollup_caps.asciidoc[] +include::rollup/get_rollup_index_caps.asciidoc[] == Security APIs