From fc224a0de8153652ed2d0e409bc2dbd8746068e8 Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Wed, 27 May 2015 12:05:32 +0200 Subject: [PATCH] Cat API: Add wildcard support for header names This adds wildcard support (simple regexes) for specifying header names. Aliases are supported as well. Closes #10811 --- docs/reference/cat.asciidoc | 6 +- .../test/cat.thread_pool/10_basic.yaml | 12 +++ .../rest/action/support/RestTable.java | 48 +++++++++-- .../rest/action/support/RestTableTest.java | 82 +++++++++++++++++++ 4 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 src/test/java/org/elasticsearch/rest/action/support/RestTableTest.java diff --git a/docs/reference/cat.asciidoc b/docs/reference/cat.asciidoc index d857006d2b1..bc29cc92d64 100644 --- a/docs/reference/cat.asciidoc +++ b/docs/reference/cat.asciidoc @@ -66,6 +66,10 @@ only those columns to appear. 192.168.56.30 9300 43.9 Ramsey, Doug -------------------------------------------------- +You can also request multiple columns using simple wildcards like +`/_cat/thread_pool?h=ip,bulk.*` to get all headers (or aliases) starting +with `bulk.`. + [float] [[numeric-formats]] === Numeric formats @@ -120,4 +124,4 @@ include::cat/thread_pool.asciidoc[] include::cat/shards.asciidoc[] -include::cat/segments.asciidoc[] \ No newline at end of file +include::cat/segments.asciidoc[] diff --git a/rest-api-spec/test/cat.thread_pool/10_basic.yaml b/rest-api-spec/test/cat.thread_pool/10_basic.yaml index edb87ce27b9..37994201191 100755 --- a/rest-api-spec/test/cat.thread_pool/10_basic.yaml +++ b/rest-api-spec/test/cat.thread_pool/10_basic.yaml @@ -29,6 +29,18 @@ / #pid id host ip port ^ (\d+ \s+ \S{4} \s+ \S+ \s+ (\d{1,3}\.){3}\d{1,3} \s+ (\d+|-) \s+ \n)+ $/ + + - do: + cat.thread_pool: + h: bulk.m* + + - match: + $body: | + /^ bulk.max \s+ bulk.min \s+ \n + (\s+ \d+ \s+ \d+ \s+ \n)+ $/ + +#(\s+ \d+ \s+ \d+ \n)+ $/ + - do: cat.thread_pool: h: id,ba,fa,gea,ga,ia,maa,ma,oa,pa diff --git a/src/main/java/org/elasticsearch/rest/action/support/RestTable.java b/src/main/java/org/elasticsearch/rest/action/support/RestTable.java index 27141aa08df..0b5b44112f4 100644 --- a/src/main/java/org/elasticsearch/rest/action/support/RestTable.java +++ b/src/main/java/org/elasticsearch/rest/action/support/RestTable.java @@ -24,6 +24,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.Table; import org.elasticsearch.common.io.UTF8StreamWriter; import org.elasticsearch.common.io.stream.BytesStreamOutput; +import org.elasticsearch.common.regex.Regex; import org.elasticsearch.common.unit.ByteSizeValue; import org.elasticsearch.common.unit.SizeValue; import org.elasticsearch.common.unit.TimeValue; @@ -32,8 +33,7 @@ import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.*; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; +import java.util.*; /** */ @@ -96,11 +96,12 @@ public class RestTable { return new BytesRestResponse(RestStatus.OK, BytesRestResponse.TEXT_CONTENT_TYPE, bytesOut.bytes()); } - private static List buildDisplayHeaders(Table table, RestRequest request) { - String pHeaders = request.param("h"); + static List buildDisplayHeaders(Table table, RestRequest request) { List display = new ArrayList<>(); - if (pHeaders != null) { - for (String possibility : Strings.splitStringByCommaToArray(pHeaders)) { + if (request.hasParam("h")) { + Set headers = expandHeadersFromRequest(table, request); + + for (String possibility : headers) { DisplayHeader dispHeader = null; if (table.getAsMap().containsKey(possibility)) { @@ -147,6 +148,41 @@ public class RestTable { return display; } + /** + * Extracts all the required fields from the RestRequest 'h' parameter. In order to support wildcards like + * 'bulk.*' this needs potentially parse all the configured headers and its aliases and needs to ensure + * that everything is only added once to the returned headers, even if 'h=bulk.*.bulk.*' is specified + * or some headers are contained twice due to matching aliases + */ + private static Set expandHeadersFromRequest(Table table, RestRequest request) { + Set headers = new LinkedHashSet<>(table.getHeaders().size()); + + Map headerMap = table.getHeaderMap(); + // check headers and aliases + for (String header : Strings.splitStringByCommaToArray(request.param("h"))) { + if (Regex.isSimpleMatchPattern(header)) { + for (Map.Entry configuredHeaderEntry : headerMap.entrySet()) { + String configuredHeader = configuredHeaderEntry.getKey(); + if (Regex.simpleMatch(header, configuredHeader)) { + headers.add(configuredHeader); + } else if (configuredHeaderEntry.getValue().attr.containsKey("alias")) { + String[] aliases = Strings.splitStringByCommaToArray(configuredHeaderEntry.getValue().attr.get("alias")); + for (String alias : aliases) { + if (Regex.simpleMatch(header, alias)) { + headers.add(configuredHeader); + break; + } + } + } + } + } else { + headers.add(header); + } + } + + return headers; + } + public static int[] buildHelpWidths(Table table, RestRequest request) { int[] width = new int[3]; for (Table.Cell cell : table.getHeaders()) { diff --git a/src/test/java/org/elasticsearch/rest/action/support/RestTableTest.java b/src/test/java/org/elasticsearch/rest/action/support/RestTableTest.java new file mode 100644 index 00000000000..bdf0a07cbf5 --- /dev/null +++ b/src/test/java/org/elasticsearch/rest/action/support/RestTableTest.java @@ -0,0 +1,82 @@ +/* + * 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.rest.action.support; + +import org.elasticsearch.common.Table; +import org.elasticsearch.test.ElasticsearchTestCase; +import org.elasticsearch.test.rest.FakeRestRequest; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.elasticsearch.rest.action.support.RestTable.buildDisplayHeaders; +import static org.hamcrest.Matchers.*; + +public class RestTableTest extends ElasticsearchTestCase { + + private Table table = new Table(); + private FakeRestRequest restRequest = new FakeRestRequest(); + + @Before + public void setup() { + table.startHeaders(); + table.addCell("bulk.foo", "alias:f;desc:foo"); + table.addCell("bulk.bar", "alias:b;desc:bar"); + // should be matched as well due to the aliases + table.addCell("aliasedBulk", "alias:bulkWhatever;desc:bar"); + table.addCell("aliasedSecondBulk", "alias:foobar,bulkolicious,bulkotastic;desc:bar"); + // no match + table.addCell("unmatched", "alias:un.matched;desc:bar"); + // invalid alias + table.addCell("invalidAliasesBulk", "alias:,,,;desc:bar"); + table.endHeaders(); + } + + @Test + public void testThatDisplayHeadersSupportWildcards() throws Exception { + restRequest.params().put("h", "bulk*"); + List headers = buildDisplayHeaders(table, restRequest); + + List headerNames = getHeaderNames(headers); + assertThat(headerNames, containsInAnyOrder("bulk.foo", "bulk.bar", "aliasedBulk", "aliasedSecondBulk")); + assertThat(headerNames, not(hasItem("unmatched"))); + } + + @Test + public void testThatDisplayHeadersAreNotAddedTwice() throws Exception { + restRequest.params().put("h", "nonexistent,bulk*,bul*"); + List headers = buildDisplayHeaders(table, restRequest); + + List headerNames = getHeaderNames(headers); + assertThat(headerNames, containsInAnyOrder("bulk.foo", "bulk.bar", "aliasedBulk", "aliasedSecondBulk")); + assertThat(headerNames, not(hasItem("unmatched"))); + } + + private List getHeaderNames(List headers) { + List headerNames = new ArrayList<>(); + for (RestTable.DisplayHeader header : headers) { + headerNames.add(header.name); + } + + return headerNames; + } +}