Skip to content

Commit 73da7cd

Browse files
committed
Expand get-views to cover use cases for listing views
1 parent b0ae25c commit 73da7cd

File tree

6 files changed

+159
-108
lines changed

6 files changed

+159
-108
lines changed

rest-api-spec/src/main/resources/rest-api-spec/api/esql.get_view.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,16 @@
2323
],
2424
"parts": {
2525
"name": {
26-
"type": "string",
27-
"description": "The name of the view to get"
26+
"type": "list",
27+
"description": "A comma-separated list of view names"
2828
}
2929
}
30+
},
31+
{
32+
"path": "/_query/view",
33+
"methods": [
34+
"GET"
35+
]
3036
}
3137
]
3238
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/view/GetViewAction.java

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,24 @@
66
*/
77
package org.elasticsearch.xpack.esql.view;
88

9-
import org.elasticsearch.action.ActionRequest;
10-
import org.elasticsearch.action.ActionRequestValidationException;
119
import org.elasticsearch.action.ActionResponse;
1210
import org.elasticsearch.action.ActionType;
1311
import org.elasticsearch.action.support.TransportAction;
12+
import org.elasticsearch.action.support.local.LocalClusterStateRequest;
1413
import org.elasticsearch.common.io.stream.StreamInput;
1514
import org.elasticsearch.common.io.stream.StreamOutput;
15+
import org.elasticsearch.common.xcontent.ChunkedToXContentHelper;
16+
import org.elasticsearch.core.TimeValue;
1617
import org.elasticsearch.xcontent.ToXContentObject;
1718
import org.elasticsearch.xcontent.XContentBuilder;
1819

1920
import java.io.IOException;
21+
import java.util.List;
22+
import java.util.Map;
2023
import java.util.Objects;
24+
import java.util.TreeMap;
25+
26+
import static org.elasticsearch.xpack.esql.view.ViewMetadata.VIEWS;
2127

2228
public class GetViewAction extends ActionType<GetViewAction.Response> {
2329

@@ -28,56 +34,48 @@ private GetViewAction() {
2834
super(NAME);
2935
}
3036

31-
public static class Request extends ActionRequest {
32-
private String name;
37+
public static class Request extends LocalClusterStateRequest {
38+
private List<String> names;
3339

34-
public Request(String name) {
35-
this.name = Objects.requireNonNull(name, "name cannot be null");
40+
public Request(TimeValue masterNodeTimeout, String... names) {
41+
super(masterNodeTimeout);
42+
this.names = List.of(names);
3643
}
3744

3845
public Request(StreamInput in) throws IOException {
3946
super(in);
4047
}
4148

42-
@Override
43-
public void writeTo(StreamOutput out) throws IOException {
44-
super.writeTo(out);
45-
out.writeString(name);
46-
}
47-
48-
public String name() {
49-
return name;
50-
}
51-
52-
@Override
53-
public ActionRequestValidationException validate() {
54-
return null;
49+
public List<String> names() {
50+
return names;
5551
}
5652

5753
@Override
5854
public boolean equals(Object o) {
5955
if (this == o) return true;
6056
if (o == null || getClass() != o.getClass()) return false;
6157
Request request = (Request) o;
62-
return name.equals(request.name);
58+
return Objects.equals(names, request.names);
6359
}
6460

6561
@Override
6662
public int hashCode() {
67-
return name.hashCode();
63+
return Objects.hash(names);
6864
}
6965
}
7066

7167
public static class Response extends ActionResponse implements ToXContentObject {
7268

73-
private final View view;
69+
private final Map<String, View> views;
7470

75-
public Response(final View view) {
76-
this.view = view;
71+
public Response(Map<String, View> views) {
72+
Objects.requireNonNull(views, "views cannot be null");
73+
// use a treemap to guarantee ordering in the set, then transform it to the list of named policies
74+
this.views = new TreeMap<>(views);
7775
}
7876

79-
public View getView() {
80-
return view;
77+
public Map<String, View> getViews() {
78+
return views;
8179
}
8280

8381
@Override
@@ -88,7 +86,10 @@ public void writeTo(StreamOutput out) throws IOException {
8886
@Override
8987
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
9088
builder.startObject();
91-
view.toXContent(builder, params);
89+
var v = ChunkedToXContentHelper.xContentObjectFieldObjects(VIEWS.getPreferredName(), views);
90+
while (v.hasNext()) {
91+
v.next().toXContent(builder, params);
92+
}
9293
builder.endObject();
9394
return builder;
9495
}
@@ -101,17 +102,17 @@ public boolean equals(Object o) {
101102
if (o == null || getClass() != o.getClass()) {
102103
return false;
103104
}
104-
return view.equals(((Response) o).view);
105+
return views.equals(((Response) o).views);
105106
}
106107

107108
@Override
108109
public int hashCode() {
109-
return view.hashCode();
110+
return views.hashCode();
110111
}
111112

112113
@Override
113114
public String toString() {
114-
return "GetViewAction.Response{view=" + view.toString() + '}';
115+
return "GetViewAction.Response{" + views.toString() + '}';
115116
}
116117
}
117118
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/view/RestGetViewAction.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
package org.elasticsearch.xpack.esql.view;
99

1010
import org.elasticsearch.client.internal.node.NodeClient;
11+
import org.elasticsearch.common.Strings;
1112
import org.elasticsearch.rest.BaseRestHandler;
1213
import org.elasticsearch.rest.RestRequest;
14+
import org.elasticsearch.rest.RestUtils;
1315
import org.elasticsearch.rest.Scope;
1416
import org.elasticsearch.rest.ServerlessScope;
17+
import org.elasticsearch.rest.action.RestCancellableNodeClient;
1518
import org.elasticsearch.rest.action.RestToXContentListener;
1619

1720
import java.io.IOException;
@@ -23,7 +26,7 @@
2326
public class RestGetViewAction extends BaseRestHandler {
2427
@Override
2528
public List<Route> routes() {
26-
return List.of(new Route(GET, "/_query/view/{name}"));
29+
return List.of(new Route(GET, "/_query/view/{name}"), new Route(GET, "/_query/view"));
2730
}
2831

2932
@Override
@@ -33,7 +36,14 @@ public String getName() {
3336

3437
@Override
3538
protected RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
36-
GetViewAction.Request req = new GetViewAction.Request(request.param("name"));
37-
return channel -> client.execute(TransportGetViewAction.TYPE, req, new RestToXContentListener<>(channel));
39+
GetViewAction.Request req = new GetViewAction.Request(
40+
RestUtils.getMasterNodeTimeout(request),
41+
Strings.splitStringByCommaToArray(request.param("name"))
42+
);
43+
return channel -> new RestCancellableNodeClient(client, request.getHttpChannel()).execute(
44+
GetViewAction.INSTANCE,
45+
req,
46+
new RestToXContentListener<>(channel)
47+
);
3848
}
3949
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/view/TransportGetViewAction.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
import org.elasticsearch.tasks.Task;
1717
import org.elasticsearch.transport.TransportService;
1818

19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.TreeMap;
22+
1923
public class TransportGetViewAction extends HandledTransportAction<GetViewAction.Request, GetViewAction.Response> {
2024
public static final ActionType<RemoteInfoResponse> TYPE = new ActionType<>(GetViewAction.NAME);
2125
private final ClusterViewService viewService;
@@ -28,11 +32,24 @@ public TransportGetViewAction(TransportService transportService, ActionFilters a
2832

2933
@Override
3034
protected void doExecute(Task task, GetViewAction.Request request, ActionListener<GetViewAction.Response> listener) {
31-
View view = viewService.get(request.name());
32-
if (view == null) {
33-
listener.onFailure(new IllegalArgumentException("View [" + request.name() + "] does not exist"));
35+
TreeMap<String, View> views = new TreeMap<>();
36+
List<String> missing = new ArrayList<>();
37+
var names = request.names();
38+
if (names.isEmpty()) {
39+
names = new ArrayList<>(viewService.list());
40+
}
41+
for (String name : names) {
42+
View view = viewService.get(name);
43+
if (view == null) {
44+
missing.add(name);
45+
} else {
46+
views.put(name, view);
47+
}
48+
}
49+
if (missing.isEmpty() == false) {
50+
listener.onFailure(new IllegalArgumentException("Views do not exist: " + String.join(", ", missing)));
3451
} else {
35-
listener.onResponse(new GetViewAction.Response(view));
52+
listener.onResponse(new GetViewAction.Response(views));
3653
}
3754
}
3855
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/view/AbstractViewTestCase.java

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
1111
import org.elasticsearch.cluster.project.ProjectResolver;
1212
import org.elasticsearch.cluster.service.ClusterService;
13+
import org.elasticsearch.core.TimeValue;
1314
import org.elasticsearch.features.FeatureService;
1415
import org.elasticsearch.indices.TestIndexNameExpressionResolver;
1516
import org.elasticsearch.plugins.Plugin;
@@ -18,6 +19,7 @@
1819

1920
import java.util.Collection;
2021
import java.util.List;
22+
import java.util.Map;
2123
import java.util.concurrent.CountDownLatch;
2224
import java.util.concurrent.atomic.AtomicReference;
2325

@@ -37,30 +39,47 @@ protected ViewService viewService() {
3739
return new ClusterViewService(new EsqlFunctionRegistry(), clusterService, featureService, projectResolver, DEFAULT);
3840
}
3941

40-
protected AtomicReference<Exception> saveView(String name, View policy, ViewService viewService) throws InterruptedException {
41-
IndexNameExpressionResolver resolver = TestIndexNameExpressionResolver.newInstance();
42-
TestResponseCapture responseCapture = new TestResponseCapture();
43-
viewService.put(name, policy, responseCapture);
44-
responseCapture.latch.await();
45-
return responseCapture.error;
46-
}
42+
protected class TestViewsApi {
43+
protected ViewService viewService = viewService();
44+
45+
protected AtomicReference<Exception> save(String name, View policy) throws InterruptedException {
46+
IndexNameExpressionResolver resolver = TestIndexNameExpressionResolver.newInstance();
47+
TestResponseCapture<Void> responseCapture = new TestResponseCapture<>();
48+
viewService.put(name, policy, responseCapture);
49+
responseCapture.latch.await();
50+
return responseCapture.error;
51+
}
52+
53+
protected void delete(String name) throws Exception {
54+
TestResponseCapture<Void> responseCapture = new TestResponseCapture<>();
55+
viewService.delete(name, responseCapture);
56+
responseCapture.latch.await();
57+
if (responseCapture.error.get() != null) {
58+
throw responseCapture.error.get();
59+
}
60+
}
4761

48-
protected void deleteView(String name, ViewService viewService) throws Exception {
49-
TestResponseCapture responseCapture = new TestResponseCapture();
50-
viewService.delete(name, responseCapture);
51-
responseCapture.latch.await();
52-
if (responseCapture.error.get() != null) {
53-
throw responseCapture.error.get();
62+
public Map<String, View> get(String... names) throws Exception {
63+
TestResponseCapture<GetViewAction.Response> responseCapture = new TestResponseCapture<>();
64+
TransportGetViewAction getViewAction = getInstanceFromNode(TransportGetViewAction.class);
65+
GetViewAction.Request request = new GetViewAction.Request(TimeValue.THIRTY_SECONDS, names);
66+
getViewAction.doExecute(null, request, responseCapture);
67+
if (responseCapture.error.get() != null) {
68+
throw responseCapture.error.get();
69+
}
70+
return responseCapture.response.getViews();
5471
}
5572
}
5673

57-
protected static class TestResponseCapture implements ActionListener<Void> {
74+
protected static class TestResponseCapture<T> implements ActionListener<T> {
5875
CountDownLatch latch = new CountDownLatch(1);
5976
AtomicReference<Exception> error = new AtomicReference<>();
77+
T response;
6078

6179
@Override
62-
public void onResponse(Void unused) {
80+
public void onResponse(T response) {
6381
latch.countDown();
82+
this.response = response;
6483
}
6584

6685
@Override

0 commit comments

Comments
 (0)