diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_data_streams.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_data_streams.json index 42415068d4a5d..4af892e101b22 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_data_streams.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_data_streams.json @@ -20,8 +20,8 @@ ], "parts":{ "name":{ - "type":"list", - "description":"The comma separated names of data streams" + "type":"string", + "description":"The name or wildcard expression of the requested data streams" } } } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.data_stream/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.data_stream/10_basic.yml index 52e56003fb166..00aa3f7efd7a2 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.data_stream/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.data_stream/10_basic.yml @@ -19,7 +19,8 @@ - is_true: acknowledged - do: - indices.get_data_streams: {} + indices.get_data_streams: + name: "*" - match: { 0.name: simple-data-stream1 } - match: { 0.timestamp_field: '@timestamp' } - length: { 0.indices: 1 } @@ -54,3 +55,67 @@ - match: { status: 400 } - match: { error.root_cause.0.type: "illegal_argument_exception" } + +--- +"Get data stream": + - skip: + version: " - 7.99.99" + reason: available only in 7.7+ + + - do: + indices.create_data_stream: + name: get-data-stream1 + body: + timestamp_field: "@timestamp" + - is_true: acknowledged + + - do: + indices.create_data_stream: + name: get-data-stream2 + body: + timestamp_field: "@timestamp2" + - is_true: acknowledged + + - do: + indices.get_data_streams: {} + - match: { 0.name: get-data-stream1 } + - match: { 0.timestamp_field: '@timestamp' } + - match: { 1.name: get-data-stream2 } + - match: { 1.timestamp_field: '@timestamp2' } + + - do: + indices.get_data_streams: + name: get-data-stream1 + - match: { 0.name: get-data-stream1 } + - match: { 0.timestamp_field: '@timestamp' } + + - do: + indices.get_data_streams: + name: get-data-* + - match: { 0.name: get-data-stream1 } + - match: { 0.timestamp_field: '@timestamp' } + - match: { 1.name: get-data-stream2 } + - match: { 1.timestamp_field: '@timestamp2' } + + - do: + indices.get_data_streams: + name: nonexistent-data-stream + catch: missing + + - match: { status: 404 } + - match: { error.root_cause.0.type: "resource_not_found_exception" } + + - do: + indices.get_data_streams: + name: nonexistent* + - match: { $body: [] } + + - do: + indices.delete_data_stream: + name: get-data-stream1 + - is_true: acknowledged + + - do: + indices.delete_data_stream: + name: get-data-stream2 + - is_true: acknowledged diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/datastream/GetDataStreamsAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/datastream/GetDataStreamsAction.java index 91bef7f49c23f..61b49a2a1ba72 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/datastream/GetDataStreamsAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/datastream/GetDataStreamsAction.java @@ -18,6 +18,7 @@ */ package org.elasticsearch.action.admin.indices.datastream; +import org.elasticsearch.ResourceNotFoundException; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.ActionRequestValidationException; import org.elasticsearch.action.ActionResponse; @@ -43,7 +44,7 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -59,10 +60,10 @@ private GetDataStreamsAction() { public static class Request extends MasterNodeReadRequest { - private final String[] names; + private final String name; - public Request(String[] names) { - this.names = Objects.requireNonNull(names); + public Request(String name) { + this.name = name; } @Override @@ -72,13 +73,13 @@ public ActionRequestValidationException validate() { public Request(StreamInput in) throws IOException { super(in); - this.names = in.readStringArray(); + this.name = in.readOptionalString(); } @Override public void writeTo(StreamOutput out) throws IOException { super.writeTo(out); - out.writeStringArray(names); + out.writeOptionalString(name); } @Override @@ -86,12 +87,12 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Request request = (Request) o; - return Arrays.equals(names, request.names); + return Objects.equals(name, request.name); } @Override public int hashCode() { - return Arrays.hashCode(names); + return Objects.hash(name); } } @@ -164,22 +165,21 @@ static List getDataStreams(ClusterState clusterState, Request reques Map dataStreams = clusterState.metadata().dataStreams(); // return all data streams if no name was specified - if (request.names.length == 0) { - return new ArrayList<>(dataStreams.values()); - } + final String requestedName = request.name == null ? "*" : request.name; final List results = new ArrayList<>(); - for (String name : request.names) { - if (Regex.isSimpleMatchPattern(name)) { + if (Regex.isSimpleMatchPattern(requestedName)) { for (Map.Entry entry : dataStreams.entrySet()) { - if (Regex.simpleMatch(name, entry.getKey())) { + if (Regex.simpleMatch(requestedName, entry.getKey())) { results.add(entry.getValue()); } } - } else if (dataStreams.containsKey(name)) { - results.add(dataStreams.get(name)); + } else if (dataStreams.containsKey(request.name)) { + results.add(dataStreams.get(request.name)); + } else { + throw new ResourceNotFoundException("data_stream matching [" + request.name + "] not found"); } - } + results.sort(Comparator.comparing(DataStream::getName)); return results; } diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetDataStreamsAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetDataStreamsAction.java index fdbe63829882e..bf7a9b6e6ded7 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetDataStreamsAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetDataStreamsAction.java @@ -20,7 +20,6 @@ import org.elasticsearch.action.admin.indices.datastream.GetDataStreamsAction; import org.elasticsearch.client.node.NodeClient; -import org.elasticsearch.common.Strings; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.action.RestToXContentListener; @@ -45,8 +44,7 @@ public List routes() { @Override protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { - String[] names = Strings.splitStringByCommaToArray(request.param("name")); - GetDataStreamsAction.Request getDataStreamsRequest = new GetDataStreamsAction.Request(names); + GetDataStreamsAction.Request getDataStreamsRequest = new GetDataStreamsAction.Request(request.param("name")); return channel -> client.admin().indices().getDataStreams(getDataStreamsRequest, new RestToXContentListener<>(channel)); } } diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/datastream/GetDataStreamsRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/datastream/GetDataStreamsRequestTests.java index 21038a60607ab..71e89d8040b7e 100644 --- a/server/src/test/java/org/elasticsearch/action/admin/indices/datastream/GetDataStreamsRequestTests.java +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/datastream/GetDataStreamsRequestTests.java @@ -18,7 +18,7 @@ */ package org.elasticsearch.action.admin.indices.datastream; -import org.elasticsearch.action.ActionRequestValidationException; +import org.elasticsearch.ResourceNotFoundException; import org.elasticsearch.action.admin.indices.datastream.GetDataStreamsAction.Request; import org.elasticsearch.cluster.ClusterName; import org.elasticsearch.cluster.ClusterState; @@ -31,6 +31,7 @@ import java.util.List; import java.util.Map; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; public class GetDataStreamsRequestTests extends AbstractWireSerializingTestCase { @@ -42,31 +43,72 @@ protected Writeable.Reader instanceReader() { @Override protected Request createTestInstance() { - return new Request(generateRandomStringArray(8, 8, false)); + final String searchParameter; + switch (randomIntBetween(1, 4)) { + case 1: + searchParameter = randomAlphaOfLength(8); + break; + case 2: + searchParameter = randomAlphaOfLength(8) + "*"; + break; + case 3: + searchParameter = "*"; + break; + default: + searchParameter = null; + break; + } + return new Request(searchParameter); } - public void testValidateRequest() { - GetDataStreamsAction.Request req = new GetDataStreamsAction.Request(new String[]{}); - ActionRequestValidationException e = req.validate(); - assertNull(e); - } - - public void testGetDataStreams() { + public void testGetDataStream() { final String dataStreamName = "my-data-stream"; DataStream existingDataStream = new DataStream(dataStreamName, "timestamp", Collections.emptyList()); ClusterState cs = ClusterState.builder(new ClusterName("_name")) .metadata(Metadata.builder().dataStreams(Map.of(dataStreamName, existingDataStream)).build()).build(); - GetDataStreamsAction.Request req = new GetDataStreamsAction.Request(new String[]{dataStreamName}); + GetDataStreamsAction.Request req = new GetDataStreamsAction.Request(dataStreamName); List dataStreams = GetDataStreamsAction.TransportAction.getDataStreams(cs, req); assertThat(dataStreams.size(), equalTo(1)); assertThat(dataStreams.get(0).getName(), equalTo(dataStreamName)); } + public void testGetDataStreamsWithWildcards() { + final String[] dataStreamNames = {"my-data-stream", "another-data-stream"}; + DataStream ds1 = new DataStream(dataStreamNames[0], "timestamp", Collections.emptyList()); + DataStream ds2 = new DataStream(dataStreamNames[1], "timestamp", Collections.emptyList()); + ClusterState cs = ClusterState.builder(new ClusterName("_name")) + .metadata(Metadata.builder().dataStreams( + Map.of(dataStreamNames[0], ds1, dataStreamNames[1], ds2)).build()) + .build(); + + GetDataStreamsAction.Request req = new GetDataStreamsAction.Request(dataStreamNames[1].substring(0, 5) + "*"); + List dataStreams = GetDataStreamsAction.TransportAction.getDataStreams(cs, req); + assertThat(dataStreams.size(), equalTo(1)); + assertThat(dataStreams.get(0).getName(), equalTo(dataStreamNames[1])); + + req = new GetDataStreamsAction.Request("*"); + dataStreams = GetDataStreamsAction.TransportAction.getDataStreams(cs, req); + assertThat(dataStreams.size(), equalTo(2)); + assertThat(dataStreams.get(0).getName(), equalTo(dataStreamNames[1])); + assertThat(dataStreams.get(1).getName(), equalTo(dataStreamNames[0])); + + req = new GetDataStreamsAction.Request((String) null); + dataStreams = GetDataStreamsAction.TransportAction.getDataStreams(cs, req); + assertThat(dataStreams.size(), equalTo(2)); + assertThat(dataStreams.get(0).getName(), equalTo(dataStreamNames[1])); + assertThat(dataStreams.get(1).getName(), equalTo(dataStreamNames[0])); + + req = new GetDataStreamsAction.Request("matches-none*"); + dataStreams = GetDataStreamsAction.TransportAction.getDataStreams(cs, req); + assertThat(dataStreams.size(), equalTo(0)); + } + public void testGetNonexistentDataStream() { final String dataStreamName = "my-data-stream"; ClusterState cs = ClusterState.builder(new ClusterName("_name")).build(); - GetDataStreamsAction.Request req = new GetDataStreamsAction.Request(new String[]{dataStreamName}); - List dataStreams = GetDataStreamsAction.TransportAction.getDataStreams(cs, req); - assertThat(dataStreams.size(), equalTo(0)); + GetDataStreamsAction.Request req = new GetDataStreamsAction.Request(dataStreamName); + ResourceNotFoundException e = expectThrows(ResourceNotFoundException.class, + () -> GetDataStreamsAction.TransportAction.getDataStreams(cs, req)); + assertThat(e.getMessage(), containsString("data_stream matching [" + dataStreamName + "] not found")); } }