diff --git a/docs/changelog/83083.yaml b/docs/changelog/83083.yaml new file mode 100644 index 0000000000000..c0fed25e8ce72 --- /dev/null +++ b/docs/changelog/83083.yaml @@ -0,0 +1,6 @@ +pr: 83083 +summary: Expose 'features' option in Get Index API +area: Indices APIs +type: enhancement +issues: + - 82948 diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get.json index fb4dee07234cc..e0cd96e346a7b 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get.json @@ -50,6 +50,16 @@ "default":"open", "description":"Whether wildcard expressions should get expanded to open or closed indices (default: open)" }, + "features":{ + "type":"enum", + "options":[ + "aliases", + "mappings", + "settings" + ], + "default":"aliases,mappings,settings", + "description":"Return only information on specified index features" + }, "flat_settings":{ "type":"boolean", "description":"Return settings in flat format (default: false)" diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.get/10_basic.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.get/10_basic.yml index eb30f08abcaf1..c64b872c3e8ad 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.get/10_basic.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/indices.get/10_basic.yml @@ -169,3 +169,18 @@ setup: catch: bad_request indices.get: index: _foo + +--- +"Should return only selected features": + - skip: + version: " - 8.0.99" + reason: "features option added in 8.1.0" + + - do: + indices.get: + index: test_index + features: aliases,settings + + - is_true: test_index.aliases + - is_true: test_index.settings + - match: { test_index.mappings: {}} diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexRequest.java b/server/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexRequest.java index a493647e58851..8c821e90d9373 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexRequest.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/get/GetIndexRequest.java @@ -13,8 +13,14 @@ import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.util.ArrayUtils; +import org.elasticsearch.rest.RestRequest; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; /** * A request to retrieve information about an index. @@ -50,9 +56,33 @@ public static Feature fromId(byte id) { } return FEATURES[id]; } + + public static Feature[] fromRequest(RestRequest request) { + if (request.hasParam("features")) { + String[] featureNames = request.param("features").split(","); + Set features = new HashSet<>(); + List invalidFeatures = new ArrayList<>(); + for (int k = 0; k < featureNames.length; k++) { + try { + features.add(Feature.valueOf(featureNames[k].toUpperCase(Locale.ROOT))); + } catch (IllegalArgumentException e) { + invalidFeatures.add(featureNames[k]); + } + } + if (invalidFeatures.size() > 0) { + throw new IllegalArgumentException( + String.format(Locale.ROOT, "Invalid features specified [%s]", String.join(",", invalidFeatures)) + ); + } else { + return features.toArray(Feature[]::new); + } + } else { + return DEFAULT_FEATURES; + } + } } - private static final Feature[] DEFAULT_FEATURES = new Feature[] { Feature.ALIASES, Feature.MAPPINGS, Feature.SETTINGS }; + static final Feature[] DEFAULT_FEATURES = new Feature[] { Feature.ALIASES, Feature.MAPPINGS, Feature.SETTINGS }; private Feature[] features = DEFAULT_FEATURES; private boolean humanReadable = false; private transient boolean includeDefaults = false; diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndicesAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndicesAction.java index 3e55b84a3cbbc..6bd1e35787ba5 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndicesAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetIndicesAction.java @@ -69,6 +69,7 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC getIndexRequest.masterNodeTimeout(request.paramAsTime("master_timeout", getIndexRequest.masterNodeTimeout())); getIndexRequest.humanReadable(request.paramAsBoolean("human", false)); getIndexRequest.includeDefaults(request.paramAsBoolean("include_defaults", false)); + getIndexRequest.features(GetIndexRequest.Feature.fromRequest(request)); return channel -> client.admin().indices().getIndex(getIndexRequest, new RestToXContentListener<>(channel)); } diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/get/GetIndexRequestTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/get/GetIndexRequestTests.java new file mode 100644 index 0000000000000..934711dfa7ec6 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/get/GetIndexRequestTests.java @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.action.admin.indices.get; + +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.rest.RestRequestTests; +import org.elasticsearch.test.ESTestCase; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import static org.hamcrest.Matchers.arrayContainingInAnyOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; + +public class GetIndexRequestTests extends ESTestCase { + + public void testFeaturesFromRequest() { + int numFeatures = randomIntBetween(1, GetIndexRequest.DEFAULT_FEATURES.length); + List featureNames = new ArrayList<>(); + List expectedFeatures = new ArrayList<>(); + for (int k = 0; k < numFeatures; k++) { + GetIndexRequest.Feature feature = randomValueOtherThanMany( + f -> featureNames.contains(f.name()), + () -> randomFrom(GetIndexRequest.DEFAULT_FEATURES) + ); + featureNames.add(feature.name()); + expectedFeatures.add(feature); + } + + RestRequest request = RestRequestTests.contentRestRequest("", Map.of("features", String.join(",", featureNames))); + GetIndexRequest.Feature[] featureArray = GetIndexRequest.Feature.fromRequest(request); + assertThat(featureArray, arrayContainingInAnyOrder(expectedFeatures.toArray(GetIndexRequest.Feature[]::new))); + } + + public void testDuplicateFeatures() { + int numFeatures = randomIntBetween(1, 5); + GetIndexRequest.Feature feature = randomFrom(GetIndexRequest.DEFAULT_FEATURES); + List featureList = new ArrayList<>(); + for (int k = 0; k < numFeatures; k++) { + featureList.add(feature.name()); + } + RestRequest request = RestRequestTests.contentRestRequest("", Map.of("features", String.join(",", featureList))); + GetIndexRequest.Feature[] features = GetIndexRequest.Feature.fromRequest(request); + assertThat(features.length, equalTo(1)); + assertThat(features[0], equalTo(feature)); + } + + public void testMissingFeatures() { + RestRequest request = RestRequestTests.contentRestRequest("", Map.of()); + GetIndexRequest.Feature[] features = GetIndexRequest.Feature.fromRequest(request); + assertThat(features, arrayContainingInAnyOrder(GetIndexRequest.DEFAULT_FEATURES)); + } + + public void testInvalidFeatures() { + int numFeatures = randomIntBetween(1, 4); + List invalidFeatures = new ArrayList<>(); + for (int k = 0; k < numFeatures; k++) { + invalidFeatures.add(randomAlphaOfLength(5)); + } + + RestRequest request = RestRequestTests.contentRestRequest("", Map.of("features", String.join(",", invalidFeatures))); + IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> GetIndexRequest.Feature.fromRequest(request)); + assertThat( + e.getMessage(), + containsString(String.format(Locale.ROOT, "Invalid features specified [%s]", String.join(",", invalidFeatures))) + ); + } +} diff --git a/server/src/test/java/org/elasticsearch/rest/RestRequestTests.java b/server/src/test/java/org/elasticsearch/rest/RestRequestTests.java index 8913034cd626a..d2799efef0ec7 100644 --- a/server/src/test/java/org/elasticsearch/rest/RestRequestTests.java +++ b/server/src/test/java/org/elasticsearch/rest/RestRequestTests.java @@ -236,7 +236,7 @@ public void testRequiredContent() { assertEquals("unknown content type", e.getMessage()); } - private static RestRequest contentRestRequest(String content, Map params) { + public static RestRequest contentRestRequest(String content, Map params) { Map> headers = new HashMap<>(); headers.put("Content-Type", Collections.singletonList("application/json")); return contentRestRequest(content, params, headers); @@ -250,7 +250,7 @@ private static RestRequest contentRestRequest(String content, Map