[HLRC] Add GetRollupIndexCaps API (#35102)

Also refactors the caps response tests a bit to share the same
abstract class to reduce duplication of test code
This commit is contained in:
Zachary Tong 2018-11-12 15:05:03 -05:00 committed by GitHub
parent e7896bcefc
commit 2da239fb5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 668 additions and 134 deletions

View File

@ -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 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/master/rollup-get-rollup-index-caps.html">
* the docs</a> 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 <a href="https://www.elastic.co/guide/en/elasticsearch/reference/master/rollup-get-rollup-index-caps.html">
* the docs</a> 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<GetRollupIndexCapsResponse> listener) {
restHighLevelClient.performRequestAsyncAndParseEntity(request,
RollupRequestConverters::getRollupIndexCaps,
options,
GetRollupIndexCapsResponse::fromXContent,
listener,
Collections.emptySet());
}
}

View File

@ -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;
}
}

View File

@ -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<String, RollableIndexCaps> 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<String, RollableIndexCaps> entry : jobs.entrySet()) {
entry.getValue().toXContent(builder, params);
}
builder.endObject();
return builder;
}
public static GetRollupCapsResponse fromXContent(final XContentParser parser) throws IOException {
Map<String, RollableIndexCaps> 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);
}
}

View File

@ -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);
}
}

View File

@ -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<String, RollableIndexCaps> jobs;
public GetRollupIndexCapsResponse(final Map<String, RollableIndexCaps> jobs) {
this.jobs = Collections.unmodifiableMap(Objects.requireNonNull(jobs));
}
public Map<String, RollableIndexCaps> getJobs() {
return jobs;
}
public static GetRollupIndexCapsResponse fromXContent(final XContentParser parser) throws IOException {
Map<String, RollableIndexCaps> 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);
}
}

View File

@ -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<Map<String, Object>> valueCaps = fieldCaps.get("value").getAggs();
assertThat(valueCaps.size(), equalTo(SUPPORTED_METRICS.size()));
}
public void testGetRollupIndexCaps() throws Exception {
final Set<Integer> 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<MetricConfig> 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<String, RollableIndexCaps> rolledPatterns = capsResponse.getJobs();
assertThat(rolledPatterns.size(), equalTo(1));
RollableIndexCaps docsPattern = rolledPatterns.get(rollupIndex);
assertThat(docsPattern.getIndexName(), equalTo(rollupIndex));
List<RollupJobCaps> 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<String, RollupJobCaps.RollupFieldCaps> fieldCaps = jobCaps.getFieldCaps();
List<Map<String, Object>> timestampCaps = fieldCaps.get("date").getAggs();
for (Map.Entry<String, Object> 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<Map<String, Object>> valueCaps = fieldCaps.get("value").getAggs();
assertThat(valueCaps.size(), equalTo(SUPPORTED_METRICS.size()));
}
}

View File

@ -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<GetRollupCapsResponse> {
private Map<String, RollableIndexCaps> 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<RollupJobCaps> 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<GetRollupCapsResponse> {
@Override
protected GetRollupCapsResponse createTestInstance() {
@ -71,82 +32,16 @@ public class GetRollupCapsResponseTests extends AbstractXContentTestCase<GetRoll
}
@Override
protected boolean supportsUnknownFields() {
return false;
protected void toXContent(GetRollupCapsResponse response, XContentBuilder builder) throws IOException {
builder.startObject();
for (Map.Entry<String, RollableIndexCaps> 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<String, RollupJobCaps.RollupFieldCaps> createRollupFieldCaps(final RollupJobConfig rollupJobConfig) {
final Map<String, List<Map<String, Object>>> tempFieldCaps = new HashMap<>();
final GroupConfig groupConfig = rollupJobConfig.getGroupConfig();
if (groupConfig != null) {
// Create RollupFieldCaps for the date histogram
final DateHistogramGroupConfig dateHistogram = groupConfig.getDateHistogram();
final Map<String, Object> 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<Map<String, Object>> 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<String, Object> histogramAggCap = new HashMap<>();
histogramAggCap.put("agg", HistogramAggregationBuilder.NAME);
histogramAggCap.put("interval", histogram.getInterval());
Arrays.stream(rollupJobConfig.getGroupConfig().getHistogram().getFields()).forEach(field -> {
List<Map<String, Object>> 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<String, Object> termsAggCap = singletonMap("agg", TermsAggregationBuilder.NAME);
Arrays.stream(rollupJobConfig.getGroupConfig().getTerms().getFields()).forEach(field -> {
List<Map<String, Object>> caps = tempFieldCaps.getOrDefault(field, new ArrayList<>());
caps.add(termsAggCap);
tempFieldCaps.put(field, caps);
});
}
}
// Create RollupFieldCaps for the metrics
final List<MetricConfig> metricsConfig = rollupJobConfig.getMetricsConfig();
if (metricsConfig.size() > 0) {
rollupJobConfig.getMetricsConfig().forEach(metricConfig -> {
final List<Map<String, Object>> metrics = metricConfig.getMetrics().stream()
.map(metric -> singletonMap("agg", (Object) metric))
.collect(Collectors.toList());
metrics.forEach(m -> {
List<Map<String, Object>> 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()))));
}
}

View File

@ -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"));
}
}

View File

@ -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<GetRollupIndexCapsResponse> {
@Override
protected GetRollupIndexCapsResponse createTestInstance() {
return new GetRollupIndexCapsResponse(indices);
}
@Override
protected void toXContent(GetRollupIndexCapsResponse response, XContentBuilder builder) throws IOException {
builder.startObject();
for (Map.Entry<String, RollableIndexCaps> entry : response.getJobs().entrySet()) {
entry.getValue().toXContent(builder, null);
}
builder.endObject();
}
@Override
protected GetRollupIndexCapsResponse fromXContent(XContentParser parser) throws IOException {
return GetRollupIndexCapsResponse.fromXContent(parser);
}
}

View File

@ -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<T> extends ESTestCase {
protected Map<String, RollableIndexCaps> 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<RollupJobCaps> 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<String, RollupJobCaps.RollupFieldCaps> createRollupFieldCaps(final RollupJobConfig rollupJobConfig) {
final Map<String, List<Map<String, Object>>> tempFieldCaps = new HashMap<>();
final GroupConfig groupConfig = rollupJobConfig.getGroupConfig();
if (groupConfig != null) {
// Create RollupFieldCaps for the date histogram
final DateHistogramGroupConfig dateHistogram = groupConfig.getDateHistogram();
final Map<String, Object> 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<Map<String, Object>> 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<String, Object> histogramAggCap = new HashMap<>();
histogramAggCap.put("agg", HistogramAggregationBuilder.NAME);
histogramAggCap.put("interval", histogram.getInterval());
Arrays.stream(rollupJobConfig.getGroupConfig().getHistogram().getFields()).forEach(field -> {
List<Map<String, Object>> 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<String, Object> termsAggCap = singletonMap("agg", TermsAggregationBuilder.NAME);
Arrays.stream(rollupJobConfig.getGroupConfig().getTerms().getFields()).forEach(field -> {
List<Map<String, Object>> caps = tempFieldCaps.getOrDefault(field, new ArrayList<>());
caps.add(termsAggCap);
tempFieldCaps.put(field, caps);
});
}
}
// Create RollupFieldCaps for the metrics
final List<MetricConfig> metricsConfig = rollupJobConfig.getMetricsConfig();
if (metricsConfig.size() > 0) {
rollupJobConfig.getMetricsConfig().forEach(metricConfig -> {
final List<Map<String, Object>> metrics = metricConfig.getMetrics().stream()
.map(metric -> singletonMap("agg", (Object) metric))
.collect(Collectors.toList());
metrics.forEach(m -> {
List<Map<String, Object>> 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()))));
}
}

View File

@ -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

View File

@ -311,12 +311,14 @@ The Java High Level REST Client supports the following Rollup APIs:
* <<{upid}-rollup-delete-job>>
* <<java-rest-high-x-pack-rollup-get-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