diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/GetAliasesResponse.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/GetAliasesResponse.java new file mode 100644 index 0000000000000..26bdcd2d26779 --- /dev/null +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/GetAliasesResponse.java @@ -0,0 +1,199 @@ +/* + * 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; + +import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.cluster.metadata.AliasMetaData; +import org.elasticsearch.common.xcontent.StatusToXContentObject; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentParser.Token; +import org.elasticsearch.rest.RestStatus; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; + +/** + * Response obtained from the get aliases API. + * The format is pretty horrible as it holds aliases, but at the same time errors can come back through the status and error fields. + * Such errors are mostly 404 - NOT FOUND for aliases that were specified but not found. In such case the client won't throw exception + * so it allows to retrieve the returned aliases, while at the same time checking if errors were returned. + * There's also the case where an exception is returned, like for instance an {@link org.elasticsearch.index.IndexNotFoundException}. + * We would usually throw such exception, but we configure the client to not throw for 404 to support the case above, hence we also not + * throw in case an index is not found, although it is a hard error that doesn't come back with aliases. + */ +public class GetAliasesResponse extends ActionResponse implements StatusToXContentObject { + + private final RestStatus status; + private final String error; + private final ElasticsearchException exception; + + private final Map> aliases; + + GetAliasesResponse(RestStatus status, String error, Map> aliases) { + this.status = status; + this.error = error; + this.aliases = aliases; + this.exception = null; + } + + private GetAliasesResponse(RestStatus status, ElasticsearchException exception) { + this.status = status; + this.error = null; + this.aliases = Collections.emptyMap(); + this.exception = exception; + } + + @Override + public RestStatus status() { + return status; + } + + /** + * Return the possibly returned error, null otherwise + */ + public String getError() { + return error; + } + + /** + * Return the exception that may have been returned + */ + public ElasticsearchException getException() { + return exception; + } + + /** + * Return the requested aliases + */ + public Map> getAliases() { + return aliases; + } + + @Override + public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(); + { + if (status != RestStatus.OK) { + builder.field("error", error); + builder.field("status", status.getStatus()); + } + + for (Map.Entry> entry : aliases.entrySet()) { + builder.startObject(entry.getKey()); + { + builder.startObject("aliases"); + { + for (final AliasMetaData alias : entry.getValue()) { + AliasMetaData.Builder.toXContent(alias, builder, ToXContent.EMPTY_PARAMS); + } + } + builder.endObject(); + } + builder.endObject(); + } + } + builder.endObject(); + return builder; + } + + /** + * Parse the get aliases response + */ + public static GetAliasesResponse fromXContent(XContentParser parser) throws IOException { + if (parser.currentToken() == null) { + parser.nextToken(); + } + ensureExpectedToken(Token.START_OBJECT, parser.currentToken(), parser::getTokenLocation); + Map> aliases = new HashMap<>(); + + String currentFieldName; + Token token; + String error = null; + ElasticsearchException exception = null; + RestStatus status = RestStatus.OK; + + while (parser.nextToken() != Token.END_OBJECT) { + if (parser.currentToken() == Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + + if ("status".equals(currentFieldName)) { + if ((token = parser.nextToken()) != Token.FIELD_NAME) { + ensureExpectedToken(Token.VALUE_NUMBER, token, parser::getTokenLocation); + status = RestStatus.fromCode(parser.intValue()); + } + } else if ("error".equals(currentFieldName)) { + token = parser.nextToken(); + if (token == Token.VALUE_STRING) { + error = parser.text(); + } else if (token == Token.START_OBJECT) { + parser.nextToken(); + exception = ElasticsearchException.innerFromXContent(parser, true); + } else if (token == Token.START_ARRAY) { + parser.skipChildren(); + } + } else { + String indexName = parser.currentName(); + if (parser.nextToken() == Token.START_OBJECT) { + Set parseInside = parseAliases(parser); + aliases.put(indexName, parseInside); + } + } + } + } + if (exception != null) { + assert error == null; + assert aliases.isEmpty(); + return new GetAliasesResponse(status, exception); + } + return new GetAliasesResponse(status, error, aliases); + } + + private static Set parseAliases(XContentParser parser) throws IOException { + Set aliases = new HashSet<>(); + Token token; + String currentFieldName = null; + while ((token = parser.nextToken()) != Token.END_OBJECT) { + if (token == Token.FIELD_NAME) { + currentFieldName = parser.currentName(); + } else if (token == Token.START_OBJECT) { + if ("aliases".equals(currentFieldName)) { + while (parser.nextToken() != Token.END_OBJECT) { + AliasMetaData fromXContent = AliasMetaData.Builder.fromXContent(parser); + aliases.add(fromXContent); + } + } else { + parser.skipChildren(); + } + } else if (token == Token.START_ARRAY) { + parser.skipChildren(); + } + } + return aliases; + } +} diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java index 59d2cf1392d30..5d0376efce5f6 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java @@ -58,11 +58,13 @@ import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateResponse; +import org.elasticsearch.rest.RestStatus; import java.io.IOException; import java.util.Collections; import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; /** * A wrapper for the {@link RestHighLevelClient} that provides methods for accessing the Indices API. @@ -978,6 +980,33 @@ public void rolloverAsync(RolloverRequest rolloverRequest, ActionListener Indices Aliases API on + * elastic.co + * @param getAliasesRequest 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 GetAliasesResponse getAlias(GetAliasesRequest getAliasesRequest, RequestOptions options) throws IOException { + return restHighLevelClient.performRequestAndParseEntity(getAliasesRequest, RequestConverters::getAlias, options, + GetAliasesResponse::fromXContent, singleton(RestStatus.NOT_FOUND.getStatus())); + } + + /** + * Asynchronously gets one or more aliases using the Get Index Aliases API. + * See Indices Aliases API on + * elastic.co + * @param getAliasesRequest 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 getAliasAsync(GetAliasesRequest getAliasesRequest, RequestOptions options, ActionListener listener) { + restHighLevelClient.performRequestAsyncAndParseEntity(getAliasesRequest, RequestConverters::getAlias, options, + GetAliasesResponse::fromXContent, listener, singleton(RestStatus.NOT_FOUND.getStatus())); + } + /** * Updates specific index level settings using the Update Indices Settings API. * See Update Indices Settings diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java index 02b91f5298011..b9e757cea5442 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RequestConverters.java @@ -831,6 +831,17 @@ static Request putTemplate(PutIndexTemplateRequest putIndexTemplateRequest) thro return request; } + static Request getAlias(GetAliasesRequest getAliasesRequest) { + String[] indices = getAliasesRequest.indices() == null ? Strings.EMPTY_ARRAY : getAliasesRequest.indices(); + String[] aliases = getAliasesRequest.aliases() == null ? Strings.EMPTY_ARRAY : getAliasesRequest.aliases(); + String endpoint = endpoint(indices, "_alias", aliases); + Request request = new Request(HttpGet.METHOD_NAME, endpoint); + Params params = new Params(request); + params.withIndicesOptions(getAliasesRequest.indicesOptions()); + params.withLocal(getAliasesRequest.local()); + return request; + } + static Request getTemplates(GetIndexTemplatesRequest getIndexTemplatesRequest) throws IOException { String[] names = getIndexTemplatesRequest.names(); String endpoint = new EndpointBuilder().addPathPartAsIs("_template").addCommaSeparatedPathParts(names).build(); diff --git a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java index 8980508c48738..0084ce0f90d74 100644 --- a/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java +++ b/client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java @@ -1021,10 +1021,10 @@ protected final Resp performRequest(Req reques try { return responseConverter.apply(e.getResponse()); } catch (Exception innerException) { - //the exception is ignored as we now try to parse the response as an error. - //this covers cases like get where 404 can either be a valid document not found response, - //or an error for which parsing is completely different. We try to consider the 404 response as a valid one - //first. If parsing of the response breaks, we fall back to parsing it as an error. + // the exception is ignored as we now try to parse the response as an error. + // this covers cases like get where 404 can either be a valid document not found response, + // or an error for which parsing is completely different. We try to consider the 404 response as a valid one + // first. If parsing of the response breaks, we fall back to parsing it as an error. throw parseResponseException(e); } } @@ -1109,10 +1109,10 @@ public void onFailure(Exception exception) { try { actionListener.onResponse(responseConverter.apply(response)); } catch (Exception innerException) { - //the exception is ignored as we now try to parse the response as an error. - //this covers cases like get where 404 can either be a valid document not found response, - //or an error for which parsing is completely different. We try to consider the 404 response as a valid one - //first. If parsing of the response breaks, we fall back to parsing it as an error. + // the exception is ignored as we now try to parse the response as an error. + // this covers cases like get where 404 can either be a valid document not found response, + // or an error for which parsing is completely different. We try to consider the 404 response as a valid one + // first. If parsing of the response breaks, we fall back to parsing it as an error. actionListener.onFailure(parseResponseException(responseException)); } } else { diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/GetAliasesResponseTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/GetAliasesResponseTests.java new file mode 100644 index 0000000000000..5f3354ad2b95d --- /dev/null +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/GetAliasesResponseTests.java @@ -0,0 +1,175 @@ +/* + * 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; + +import org.elasticsearch.cluster.metadata.AliasMetaData; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.test.AbstractXContentTestCase; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +public class GetAliasesResponseTests extends AbstractXContentTestCase { + + @Override + protected GetAliasesResponse createTestInstance() { + RestStatus status = randomFrom(RestStatus.OK, RestStatus.NOT_FOUND); + String errorMessage = RestStatus.OK == status ? null : randomAlphaOfLengthBetween(5, 10); + return new GetAliasesResponse(status, errorMessage, createIndicesAliasesMap(0, 5)); + } + + private static Map> createIndicesAliasesMap(int min, int max) { + Map> map = new HashMap<>(); + int indicesNum = randomIntBetween(min, max); + for (int i = 0; i < indicesNum; i++) { + String index = randomAlphaOfLength(5); + Set aliasMetaData = new HashSet<>(); + int aliasesNum = randomIntBetween(0, 3); + for (int alias = 0; alias < aliasesNum; alias++) { + aliasMetaData.add(createAliasMetaData()); + } + map.put(index, aliasMetaData); + } + return map; + } + + private static AliasMetaData createAliasMetaData() { + AliasMetaData.Builder builder = AliasMetaData.builder(randomAlphaOfLengthBetween(3, 10)); + if (randomBoolean()) { + builder.routing(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.searchRouting(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.indexRouting(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.filter("{\"term\":{\"year\":2016}}"); + } + return builder.build(); + } + + @Override + protected GetAliasesResponse doParseInstance(XContentParser parser) throws IOException { + return GetAliasesResponse.fromXContent(parser); + } + + @Override + protected Predicate getRandomFieldsExcludeFilter() { + return p -> p.equals("") // do not add elements at the top-level as any element at this level is parsed as a new index + || p.endsWith(".aliases") // do not add new alias + || p.contains(".filter"); // do not insert random data into AliasMetaData#filter + } + + @Override + protected boolean supportsUnknownFields() { + return true; + } + + @Override + protected void assertEqualInstances(GetAliasesResponse expectedInstance, GetAliasesResponse newInstance) { + assertEquals(expectedInstance.getAliases(), newInstance.getAliases()); + assertEquals(expectedInstance.status(), newInstance.status()); + assertEquals(expectedInstance.getError(), newInstance.getError()); + assertNull(expectedInstance.getException()); + assertNull(newInstance.getException()); + } + + public void testFromXContentWithElasticsearchException() throws IOException { + String xContent = + "{" + + " \"error\": {" + + " \"root_cause\": [" + + " {" + + " \"type\": \"index_not_found_exception\"," + + " \"reason\": \"no such index\"," + + " \"resource.type\": \"index_or_alias\"," + + " \"resource.id\": \"index\"," + + " \"index_uuid\": \"_na_\"," + + " \"index\": \"index\"" + + " }" + + " ]," + + " \"type\": \"index_not_found_exception\"," + + " \"reason\": \"no such index\"," + + " \"resource.type\": \"index_or_alias\"," + + " \"resource.id\": \"index\"," + + " \"index_uuid\": \"_na_\"," + + " \"index\": \"index\"" + + " }," + + " \"status\": 404" + + "}"; + + try (XContentParser parser = createParser(JsonXContent.jsonXContent, xContent)) { + GetAliasesResponse getAliasesResponse = GetAliasesResponse.fromXContent(parser); + assertThat(getAliasesResponse.getError(), nullValue()); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getException().getMessage(), + equalTo("Elasticsearch exception [type=index_not_found_exception, reason=no such index]")); + } + } + + public void testFromXContentWithNoAliasFound() throws IOException { + String xContent = + "{" + + " \"error\": \"alias [aa] missing\"," + + " \"status\": 404" + + "}"; + try (XContentParser parser = createParser(JsonXContent.jsonXContent, xContent)) { + GetAliasesResponse getAliasesResponse = GetAliasesResponse.fromXContent(parser); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getError(), equalTo("alias [aa] missing")); + assertThat(getAliasesResponse.getException(), nullValue()); + } + } + + public void testFromXContentWithMissingAndFoundAlias() throws IOException { + String xContent = + "{" + + " \"error\": \"alias [something] missing\"," + + " \"status\": 404," + + " \"index\": {" + + " \"aliases\": {" + + " \"alias\": {}" + + " }" + + " }" + + "}"; + final String index = "index"; + try (XContentParser parser = createParser(JsonXContent.jsonXContent, xContent)) { + GetAliasesResponse response = GetAliasesResponse.fromXContent(parser); + assertThat(response.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(response.getError(), equalTo("alias [something] missing")); + assertThat(response.getAliases().size(), equalTo(1)); + assertThat(response.getAliases().get(index).size(), equalTo(1)); + AliasMetaData aliasMetaData = response.getAliases().get(index).iterator().next(); + assertThat(aliasMetaData.alias(), equalTo("alias")); + assertThat(response.getException(), nullValue()); + } + } +} diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java index 3945a39e8c77e..9b54938ede4d6 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/IndicesClientIT.java @@ -52,10 +52,10 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshResponse; import org.elasticsearch.action.admin.indices.rollover.RolloverRequest; import org.elasticsearch.action.admin.indices.rollover.RolloverResponse; -import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; -import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeType; @@ -67,6 +67,7 @@ import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.action.support.WriteRequest; import org.elasticsearch.action.support.broadcast.BroadcastResponse; +import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.common.ValidationException; @@ -98,6 +99,8 @@ import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.startsWith; public class IndicesClientIT extends ESRestHighLevelClientTestCase { @@ -320,28 +323,27 @@ public void testGetSettingsWithDefaultsFiltered() throws IOException { assertEquals(0, getSettingsResponse.getIndexToSettings().get("get_settings_index").size()); assertEquals(1, getSettingsResponse.getIndexToDefaultSettings().get("get_settings_index").size()); } + public void testPutMapping() throws IOException { - { - // Add mappings to index - String indexName = "mapping_index"; - createIndex(indexName, Settings.EMPTY); + // Add mappings to index + String indexName = "mapping_index"; + createIndex(indexName, Settings.EMPTY); - PutMappingRequest putMappingRequest = new PutMappingRequest(indexName); - putMappingRequest.type("type_name"); - XContentBuilder mappingBuilder = JsonXContent.contentBuilder(); - mappingBuilder.startObject().startObject("properties").startObject("field"); - mappingBuilder.field("type", "text"); - mappingBuilder.endObject().endObject().endObject(); - putMappingRequest.source(mappingBuilder); + PutMappingRequest putMappingRequest = new PutMappingRequest(indexName); + putMappingRequest.type("type_name"); + XContentBuilder mappingBuilder = JsonXContent.contentBuilder(); + mappingBuilder.startObject().startObject("properties").startObject("field"); + mappingBuilder.field("type", "text"); + mappingBuilder.endObject().endObject().endObject(); + putMappingRequest.source(mappingBuilder); - PutMappingResponse putMappingResponse = - execute(putMappingRequest, highLevelClient().indices()::putMapping, highLevelClient().indices()::putMappingAsync, - highLevelClient().indices()::putMapping, highLevelClient().indices()::putMappingAsync); - assertTrue(putMappingResponse.isAcknowledged()); + PutMappingResponse putMappingResponse = + execute(putMappingRequest, highLevelClient().indices()::putMapping, highLevelClient().indices()::putMappingAsync, + highLevelClient().indices()::putMapping, highLevelClient().indices()::putMappingAsync); + assertTrue(putMappingResponse.isAcknowledged()); - Map getIndexResponse = getAsMap(indexName); - assertEquals("text", XContentMapValues.extractValue(indexName + ".mappings.type_name.properties.field.type", getIndexResponse)); - } + Map getIndexResponse = getAsMap(indexName); + assertEquals("text", XContentMapValues.extractValue(indexName + ".mappings.type_name.properties.field.type", getIndexResponse)); } public void testGetMapping() throws IOException { @@ -853,6 +855,197 @@ public void testRollover() throws IOException { } } + public void testGetAlias() throws IOException { + { + createIndex("index1", Settings.EMPTY); + client().performRequest(HttpPut.METHOD_NAME, "/index1/_alias/alias1"); + + createIndex("index2", Settings.EMPTY); + client().performRequest(HttpPut.METHOD_NAME, "/index2/_alias/alias2"); + + createIndex("index3", Settings.EMPTY); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().aliases("alias1"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(1)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData, notNullValue()); + assertThat(aliasMetaData.alias(), equalTo("alias1")); + assertThat(aliasMetaData.getFilter(), nullValue()); + assertThat(aliasMetaData.getIndexRouting(), nullValue()); + assertThat(aliasMetaData.getSearchRouting(), nullValue()); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().aliases("alias*"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(2)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().aliases("_all"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(2)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().aliases("*"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(2)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices("_all"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(3)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + assertThat(getAliasesResponse.getAliases().get("index3").size(), equalTo(0)); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices("ind*"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(3)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + assertThat(getAliasesResponse.getAliases().get("index3").size(), equalTo(0)); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest(); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(3)); + assertThat(getAliasesResponse.getAliases().get("index1").size(), equalTo(1)); + AliasMetaData aliasMetaData1 = getAliasesResponse.getAliases().get("index1").iterator().next(); + assertThat(aliasMetaData1, notNullValue()); + assertThat(aliasMetaData1.alias(), equalTo("alias1")); + assertThat(getAliasesResponse.getAliases().get("index2").size(), equalTo(1)); + AliasMetaData aliasMetaData2 = getAliasesResponse.getAliases().get("index2").iterator().next(); + assertThat(aliasMetaData2, notNullValue()); + assertThat(aliasMetaData2.alias(), equalTo("alias2")); + assertThat(getAliasesResponse.getAliases().get("index3").size(), equalTo(0)); + } + } + + public void testGetAliasesNonExistentIndexOrAlias() throws IOException { + /* + * This test is quite extensive as this is the only way we can check that we haven't slid out of sync with the server + * because the server renders the xcontent in a spot that is difficult for us to access in a unit test. + */ + String alias = "alias"; + String index = "index"; + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices(index); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getException().getMessage(), + equalTo("Elasticsearch exception [type=index_not_found_exception, reason=no such index]")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest(alias); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getError(), equalTo("alias [" + alias + "] missing")); + } + createIndex(index, Settings.EMPTY); + client().performRequest(HttpPut.METHOD_NAME, index + "/_alias/" + alias); + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices(index, "non_existent_index"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getException().getMessage(), + equalTo("Elasticsearch exception [type=index_not_found_exception, reason=no such index]")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices(index, "non_existent_index").aliases(alias); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + assertThat(getAliasesResponse.getException().getMessage(), + equalTo("Elasticsearch exception [type=index_not_found_exception, reason=no such index]")); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices("non_existent_index*"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.getAliases().size(), equalTo(0)); + } + { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest().indices(index).aliases(alias, "non_existent_alias"); + GetAliasesResponse getAliasesResponse = execute(getAliasesRequest, highLevelClient().indices()::getAlias, + highLevelClient().indices()::getAliasAsync); + assertThat(getAliasesResponse.status(), equalTo(RestStatus.NOT_FOUND)); + + assertThat(getAliasesResponse.getAliases().size(), equalTo(1)); + assertThat(getAliasesResponse.getAliases().get(index).size(), equalTo(1)); + AliasMetaData aliasMetaData = getAliasesResponse.getAliases().get(index).iterator().next(); + assertThat(aliasMetaData, notNullValue()); + assertThat(aliasMetaData.alias(), equalTo(alias)); + /* + This is the above response in json format: + { + "error": "alias [something] missing", + "status": 404, + "index": { + "aliases": { + "alias": {} + } + } + } + */ + } + } + public void testIndexPutSettings() throws IOException { final Setting dynamicSetting = IndexMetaData.INDEX_NUMBER_OF_REPLICAS_SETTING; diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java index f2a1768679d1d..d1ef59e0267da 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/RequestConvertersTests.java @@ -94,6 +94,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.ByteSizeUnit; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.util.CollectionUtils; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentHelper; @@ -1563,6 +1564,36 @@ public void testRollover() throws IOException { assertEquals(expectedParams, request.getParameters()); } + public void testGetAlias() { + GetAliasesRequest getAliasesRequest = new GetAliasesRequest(); + + Map expectedParams = new HashMap<>(); + setRandomLocal(getAliasesRequest, expectedParams); + setRandomIndicesOptions(getAliasesRequest::indicesOptions, getAliasesRequest::indicesOptions, expectedParams); + + String[] indices = randomBoolean() ? null : randomIndicesNames(0, 2); + String[] aliases = randomBoolean() ? null : randomIndicesNames(0, 2); + getAliasesRequest.indices(indices); + getAliasesRequest.aliases(aliases); + + Request request = RequestConverters.getAlias(getAliasesRequest); + StringJoiner expectedEndpoint = new StringJoiner("/", "/", ""); + + if (false == CollectionUtils.isEmpty(indices)) { + expectedEndpoint.add(String.join(",", indices)); + } + expectedEndpoint.add("_alias"); + + if (false == CollectionUtils.isEmpty(aliases)) { + expectedEndpoint.add(String.join(",", aliases)); + } + + assertEquals(HttpGet.METHOD_NAME, request.getMethod()); + assertEquals(expectedEndpoint.toString(), request.getEndpoint()); + assertEquals(expectedParams, request.getParameters()); + assertNull(request.getEntity()); + } + public void testIndexPutSettings() throws IOException { String[] indices = randomBoolean() ? null : randomIndicesNames(0, 2); UpdateSettingsRequest updateSettingsRequest = new UpdateSettingsRequest(indices); diff --git a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java index bdaac91b374c6..fffba8e5f3f58 100644 --- a/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java +++ b/client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/IndicesClientDocumentationIT.java @@ -51,10 +51,10 @@ import org.elasticsearch.action.admin.indices.refresh.RefreshResponse; import org.elasticsearch.action.admin.indices.rollover.RolloverRequest; import org.elasticsearch.action.admin.indices.rollover.RolloverResponse; -import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; -import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest; import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest; +import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeRequest; import org.elasticsearch.action.admin.indices.shrink.ResizeResponse; import org.elasticsearch.action.admin.indices.shrink.ResizeType; @@ -66,9 +66,11 @@ import org.elasticsearch.action.support.DefaultShardOperationFailedException; import org.elasticsearch.action.support.IndicesOptions; import org.elasticsearch.client.ESRestHighLevelClientTestCase; +import org.elasticsearch.client.GetAliasesResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.client.SyncedFlushResponse; +import org.elasticsearch.cluster.metadata.AliasMetaData; import org.elasticsearch.cluster.metadata.IndexTemplateMetaData; import org.elasticsearch.cluster.metadata.MappingMetaData; import org.elasticsearch.common.collect.ImmutableOpenMap; @@ -87,6 +89,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -1726,6 +1729,75 @@ public void onFailure(Exception e) { assertTrue(latch.await(30L, TimeUnit.SECONDS)); } + public void testGetAlias() throws Exception { + RestHighLevelClient client = highLevelClient(); + + { + CreateIndexResponse createIndexResponse = client.indices().create(new CreateIndexRequest("index").alias(new Alias("alias"))); + assertTrue(createIndexResponse.isAcknowledged()); + } + + { + // tag::get-alias-request + GetAliasesRequest request = new GetAliasesRequest(); + GetAliasesRequest requestWithAlias = new GetAliasesRequest("alias1"); + GetAliasesRequest requestWithAliases = + new GetAliasesRequest(new String[]{"alias1", "alias2"}); + // end::get-alias-request + + // tag::get-alias-request-alias + request.aliases("alias"); // <1> + // end::get-alias-request-alias + // tag::get-alias-request-indices + request.indices("index"); // <1> + // end::get-alias-request-indices + + // tag::get-alias-request-indicesOptions + request.indicesOptions(IndicesOptions.lenientExpandOpen()); // <1> + // end::get-alias-request-indicesOptions + + // tag::get-alias-request-local + request.local(true); // <1> + // end::get-alias-request-local + + // tag::get-alias-execute + GetAliasesResponse response = client.indices().getAlias(request, RequestOptions.DEFAULT); + // end::get-alias-execute + + // tag::get-alias-response + Map> aliases = response.getAliases(); // <1> + // end::get-alias-response + + assertThat(response.getAliases().get("index").size(), equalTo(1)); + assertThat(response.getAliases().get("index").iterator().next().alias(), equalTo("alias")); + + // tag::get-alias-listener + ActionListener listener = + new ActionListener() { + @Override + public void onResponse(GetAliasesResponse getAliasesResponse) { + // <1> + } + + @Override + public void onFailure(Exception e) { + // <2> + } + }; + // end::get-alias-listener + + // Replace the empty listener by a blocking listener in test + final CountDownLatch latch = new CountDownLatch(1); + listener = new LatchedActionListener<>(listener, latch); + + // tag::get-alias-execute-async + client.indices().getAliasAsync(request, RequestOptions.DEFAULT, listener); // <1> + // end::get-alias-execute-async + + assertTrue(latch.await(30L, TimeUnit.SECONDS)); + } + } + public void testIndexPutSettings() throws Exception { RestHighLevelClient client = highLevelClient(); diff --git a/docs/java-rest/high-level/indices/get_alias.asciidoc b/docs/java-rest/high-level/indices/get_alias.asciidoc new file mode 100644 index 0000000000000..77a336e0a024c --- /dev/null +++ b/docs/java-rest/high-level/indices/get_alias.asciidoc @@ -0,0 +1,94 @@ +[[java-rest-high-get-alias]] +=== Get Alias API + +[[java-rest-high-get-alias-request]] +==== Get Alias Request + +The Get Alias API uses `GetAliasesRequest` as its request object. +One or more aliases can be optionally provided either at construction +time or later on through the relevant setter method. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-request] +-------------------------------------------------- + +==== Optional arguments +The following arguments can optionally be provided: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-request-alias] +-------------------------------------------------- +<1> One or more aliases to retrieve + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-request-indices] +-------------------------------------------------- +<1> The index or indices that the alias is associated with + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-request-indicesOptions] +-------------------------------------------------- +<1> Setting `IndicesOptions` controls how unavailable indices are resolved and +how wildcard expressions are expanded when looking for aliases that belong to +specified indices. + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-request-local] +-------------------------------------------------- +<1> The `local` flag (defaults to `false`) controls whether the aliases need +to be looked up in the local cluster state or in the cluster state held by +the elected master node + +[[java-rest-high-get-alias-sync]] +==== Synchronous Execution + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-execute] +-------------------------------------------------- + +[[java-rest-high-get-alias-async]] +==== Asynchronous Execution + +The asynchronous execution of a get alias request requires both a `GetAliasesRequest` +instance and an `ActionListener` instance to be passed to the asynchronous +method: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-execute-async] +-------------------------------------------------- +<1> The `GetAliasesRequest` 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 the `Boolean` response looks like: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-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 + +[[java-rest-high-get-alias-response]] +==== Get Alias Response + +The returned `GetAliasesResponse` allows to retrieve information about the +executed operation as follows: + +["source","java",subs="attributes,callouts,macros"] +-------------------------------------------------- +include-tagged::{doc-tests}/IndicesClientDocumentationIT.java[get-alias-response] +-------------------------------------------------- +<1> Retrieves a map of indices and their aliases \ No newline at end of file diff --git a/docs/java-rest/high-level/supported-apis.asciidoc b/docs/java-rest/high-level/supported-apis.asciidoc index 24f8fefff3430..465ae20619de3 100644 --- a/docs/java-rest/high-level/supported-apis.asciidoc +++ b/docs/java-rest/high-level/supported-apis.asciidoc @@ -80,6 +80,7 @@ Mapping Management:: Alias Management:: * <> * <> +* <> include::indices/create_index.asciidoc[] include::indices/delete_index.asciidoc[] @@ -98,6 +99,7 @@ include::indices/put_mapping.asciidoc[] include::indices/get_mappings.asciidoc[] include::indices/update_aliases.asciidoc[] include::indices/exists_alias.asciidoc[] +include::indices/get_alias.asciidoc[] include::indices/put_settings.asciidoc[] include::indices/get_settings.asciidoc[] include::indices/put_template.asciidoc[] diff --git a/server/src/main/java/org/elasticsearch/ElasticsearchException.java b/server/src/main/java/org/elasticsearch/ElasticsearchException.java index db26cb2a5e9ad..9a02b76b3e038 100644 --- a/server/src/main/java/org/elasticsearch/ElasticsearchException.java +++ b/server/src/main/java/org/elasticsearch/ElasticsearchException.java @@ -420,7 +420,7 @@ public static ElasticsearchException fromXContent(XContentParser parser) throws return innerFromXContent(parser, false); } - private static ElasticsearchException innerFromXContent(XContentParser parser, boolean parseRootCauses) throws IOException { + public static ElasticsearchException innerFromXContent(XContentParser parser, boolean parseRootCauses) throws IOException { XContentParser.Token token = parser.currentToken(); ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation); diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponse.java index 5a63ce8d86916..d0ad58b6e351c 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponse.java @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; public class GetAliasesResponse extends ActionResponse { @@ -42,7 +43,6 @@ public GetAliasesResponse(ImmutableOpenMap> aliases) GetAliasesResponse() { } - public ImmutableOpenMap> getAliases() { return aliases; } @@ -76,4 +76,21 @@ public void writeTo(StreamOutput out) throws IOException { } } } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + GetAliasesResponse that = (GetAliasesResponse) o; + return Objects.equals(aliases, that.aliases); + } + + @Override + public int hashCode() { + return Objects.hash(aliases); + } } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java index 6edc95f649d40..96ecde4e4c6dd 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java @@ -65,5 +65,4 @@ protected void masterOperation(GetAliasesRequest request, ClusterState state, Ac ImmutableOpenMap> result = state.metaData().findAliases(request.aliases(), concreteIndices); listener.onResponse(new GetAliasesResponse(result)); } - } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java b/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java index 92a3db82d6cc7..945c94bcd642d 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetaData.java @@ -348,5 +348,4 @@ public static AliasMetaData fromXContent(XContentParser parser) throws IOExcepti return builder.build(); } } - } diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesAction.java index e5442c9a2f43f..b24729f50d5f4 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetAliasesAction.java @@ -20,7 +20,6 @@ package org.elasticsearch.rest.action.admin.indices; import com.carrotsearch.hppc.cursors.ObjectObjectCursor; - import org.elasticsearch.action.admin.indices.alias.get.GetAliasesRequest; import org.elasticsearch.action.admin.indices.alias.get.GetAliasesResponse; import org.elasticsearch.action.support.IndicesOptions; @@ -84,6 +83,8 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC getAliasesRequest.indicesOptions(IndicesOptions.fromRequest(request, getAliasesRequest.indicesOptions())); getAliasesRequest.local(request.paramAsBoolean("local", getAliasesRequest.local())); + //we may want to move this logic to TransportGetAliasesAction but it is based on the original provided aliases, which will + //not always be available there (they may get replaced so retrieving request.aliases is not quite the same). return channel -> client.admin().indices().getAliases(getAliasesRequest, new RestBuilderListener(channel) { @Override public RestResponse buildResponse(GetAliasesResponse response, XContentBuilder builder) throws Exception { @@ -127,9 +128,11 @@ public RestResponse buildResponse(GetAliasesResponse response, XContentBuilder b status = RestStatus.NOT_FOUND; final String message; if (difference.size() == 1) { - message = String.format(Locale.ROOT, "alias [%s] missing", toNamesString(difference.iterator().next())); + message = String.format(Locale.ROOT, "alias [%s] missing", + Strings.collectionToCommaDelimitedString(difference)); } else { - message = String.format(Locale.ROOT, "aliases [%s] missing", toNamesString(difference.toArray(new String[0]))); + message = String.format(Locale.ROOT, "aliases [%s] missing", + Strings.collectionToCommaDelimitedString(difference)); } builder.field("error", message); builder.field("status", status.getStatus()); @@ -158,14 +161,4 @@ public RestResponse buildResponse(GetAliasesResponse response, XContentBuilder b }); } - private static String toNamesString(final String... names) { - if (names == null || names.length == 0) { - return ""; - } else if (names.length == 1) { - return names[0]; - } else { - return Arrays.stream(names).collect(Collectors.joining(",")); - } - } - } diff --git a/server/src/test/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponseTests.java b/server/src/test/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponseTests.java new file mode 100644 index 0000000000000..6c15c419d092c --- /dev/null +++ b/server/src/test/java/org/elasticsearch/action/admin/indices/alias/get/GetAliasesResponseTests.java @@ -0,0 +1,139 @@ +/* + * 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.action.admin.indices.alias.get; + +import org.elasticsearch.cluster.metadata.AliasMetaData; +import org.elasticsearch.cluster.metadata.AliasMetaData.Builder; +import org.elasticsearch.common.collect.ImmutableOpenMap; +import org.elasticsearch.test.AbstractStreamableTestCase; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +public class GetAliasesResponseTests extends AbstractStreamableTestCase { + + @Override + protected GetAliasesResponse createTestInstance() { + return createTestItem(); + } + + @Override + protected GetAliasesResponse createBlankInstance() { + return new GetAliasesResponse(); + } + + @Override + protected GetAliasesResponse mutateInstance(GetAliasesResponse response) { + return new GetAliasesResponse(mutateAliases(response.getAliases())); + } + + private static ImmutableOpenMap> mutateAliases(ImmutableOpenMap> aliases) { + if (aliases.isEmpty()) { + return createIndicesAliasesMap(1, 3).build(); + } + + if (randomBoolean()) { + ImmutableOpenMap.Builder> builder = ImmutableOpenMap.builder(aliases); + ImmutableOpenMap> list = createIndicesAliasesMap(1, 2).build(); + list.forEach(e -> builder.put(e.key, e.value)); + return builder.build(); + } + + Set indices = new HashSet<>(); + Iterator keys = aliases.keysIt(); + while (keys.hasNext()) { + indices.add(keys.next()); + } + + List indicesToBeModified = randomSubsetOf(randomIntBetween(1, indices.size()), indices); + ImmutableOpenMap.Builder> builder = ImmutableOpenMap.builder(); + + for (String index : indices) { + List list = new ArrayList<>(aliases.get(index)); + if (indicesToBeModified.contains(index)) { + if (randomBoolean() || list.isEmpty()) { + list.add(createAliasMetaData()); + } else { + int aliasIndex = randomInt(list.size() - 1); + AliasMetaData aliasMetaData = list.get(aliasIndex); + list.add(aliasIndex, mutateAliasMetaData(aliasMetaData)); + } + } + builder.put(index, list); + } + return builder.build(); + } + + private static GetAliasesResponse createTestItem() { + return new GetAliasesResponse(createIndicesAliasesMap(0, 5).build()); + } + + private static ImmutableOpenMap.Builder> createIndicesAliasesMap(int min, int max) { + ImmutableOpenMap.Builder> builder = ImmutableOpenMap.builder(); + int indicesNum = randomIntBetween(min, max); + for (int i = 0; i < indicesNum; i++) { + String index = randomAlphaOfLength(5); + List aliasMetaData = new ArrayList<>(); + int aliasesNum = randomIntBetween(0, 3); + for (int alias = 0; alias < aliasesNum; alias++) { + aliasMetaData.add(createAliasMetaData()); + } + builder.put(index, aliasMetaData); + } + return builder; + } + + public static AliasMetaData createAliasMetaData() { + Builder builder = AliasMetaData.builder(randomAlphaOfLengthBetween(3, 10)); + if (randomBoolean()) { + builder.routing(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.searchRouting(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.indexRouting(randomAlphaOfLengthBetween(3, 10)); + } + if (randomBoolean()) { + builder.filter("{\"term\":{\"year\":2016}}"); + } + return builder.build(); + } + + public static AliasMetaData mutateAliasMetaData(AliasMetaData alias) { + boolean changeAlias = randomBoolean(); + AliasMetaData.Builder builder = AliasMetaData.builder(changeAlias ? randomAlphaOfLengthBetween(2, 5) : alias.getAlias()); + builder.searchRouting(alias.searchRouting()); + builder.indexRouting(alias.indexRouting()); + builder.filter(alias.filter()); + + if (false == changeAlias) { + if (randomBoolean()) { + builder.searchRouting(alias.searchRouting() + randomAlphaOfLengthBetween(1, 3)); + } else { + builder.indexRouting(alias.indexRouting() + randomAlphaOfLengthBetween(1, 3)); + } + } + return builder.build(); + } +} diff --git a/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java b/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java index 724a99f2c9425..7e80810d7dd27 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java +++ b/test/framework/src/main/java/org/elasticsearch/test/XContentTestUtils.java @@ -60,7 +60,7 @@ public static Map convertToMap(ToXContent part) throws IOExcepti /** - * Compares to maps generated from XContentObjects. The order of elements in arrays is ignored. + * Compares two maps generated from XContentObjects. The order of elements in arrays is ignored. * * @return null if maps are equal or path to the element where the difference was found */