Fix get mappings HEAD requests
Get mappings HEAD requests incorrectly return a content-length header of 0. This commit addresses this by removing the special handling for get mappings HEAD requests, and just relying on the general mechanism that exists for handling HEAD requests in the REST layer. Relates #23192
This commit is contained in:
parent
9b8754e4c2
commit
dcf57f296e
|
@ -275,7 +275,6 @@ import org.elasticsearch.rest.action.admin.indices.RestRefreshAction;
|
|||
import org.elasticsearch.rest.action.admin.indices.RestRolloverIndexAction;
|
||||
import org.elasticsearch.rest.action.admin.indices.RestShrinkIndexAction;
|
||||
import org.elasticsearch.rest.action.admin.indices.RestSyncedFlushAction;
|
||||
import org.elasticsearch.rest.action.admin.indices.RestTypesExistsAction;
|
||||
import org.elasticsearch.rest.action.admin.indices.RestUpdateSettingsAction;
|
||||
import org.elasticsearch.rest.action.admin.indices.RestUpgradeAction;
|
||||
import org.elasticsearch.rest.action.admin.indices.RestValidateQueryAction;
|
||||
|
@ -547,7 +546,6 @@ public class ActionModule extends AbstractModule {
|
|||
registerHandler.accept(new RestGetAllAliasesAction(settings, restController));
|
||||
registerHandler.accept(new RestGetAllMappingsAction(settings, restController));
|
||||
registerHandler.accept(new RestGetAllSettingsAction(settings, restController, indexScopedSettings, settingsFilter));
|
||||
registerHandler.accept(new RestTypesExistsAction(settings, restController));
|
||||
registerHandler.accept(new RestGetIndicesAction(settings, restController, indexScopedSettings, settingsFilter));
|
||||
registerHandler.accept(new RestIndicesStatsAction(settings, restController));
|
||||
registerHandler.accept(new RestIndicesSegmentsAction(settings, restController));
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.rest.action.admin.indices;
|
||||
|
||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest;
|
||||
|
@ -28,7 +29,9 @@ import org.elasticsearch.client.node.NodeClient;
|
|||
import org.elasticsearch.cluster.metadata.MappingMetaData;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||
import org.elasticsearch.common.regex.Regex;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.set.Sets;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.index.IndexNotFoundException;
|
||||
import org.elasticsearch.indices.TypeMissingException;
|
||||
|
@ -37,21 +40,33 @@ import org.elasticsearch.rest.BytesRestResponse;
|
|||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.RestResponse;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.rest.action.RestBuilderListener;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||
import static org.elasticsearch.rest.RestRequest.Method.HEAD;
|
||||
import static org.elasticsearch.rest.RestStatus.OK;
|
||||
|
||||
public class RestGetMappingAction extends BaseRestHandler {
|
||||
public RestGetMappingAction(Settings settings, RestController controller) {
|
||||
|
||||
public RestGetMappingAction(final Settings settings, final RestController controller) {
|
||||
super(settings);
|
||||
controller.registerHandler(GET, "/{index}/{type}/_mapping", this);
|
||||
controller.registerHandler(GET, "/{index}/_mappings", this);
|
||||
controller.registerHandler(GET, "/{index}/_mapping", this);
|
||||
controller.registerHandler(GET, "/{index}/_mappings/{type}", this);
|
||||
controller.registerHandler(GET, "/{index}/_mapping/{type}", this);
|
||||
controller.registerHandler(HEAD, "/{index}/_mapping/{type}", this);
|
||||
controller.registerHandler(GET, "/_mapping/{type}", this);
|
||||
}
|
||||
|
||||
|
@ -64,48 +79,87 @@ public class RestGetMappingAction extends BaseRestHandler {
|
|||
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
|
||||
final String[] indices = Strings.splitStringByCommaToArray(request.param("index"));
|
||||
final String[] types = request.paramAsStringArrayOrEmptyIfAll("type");
|
||||
GetMappingsRequest getMappingsRequest = new GetMappingsRequest();
|
||||
final GetMappingsRequest getMappingsRequest = new GetMappingsRequest();
|
||||
getMappingsRequest.indices(indices).types(types);
|
||||
getMappingsRequest.indicesOptions(IndicesOptions.fromRequest(request, getMappingsRequest.indicesOptions()));
|
||||
getMappingsRequest.local(request.paramAsBoolean("local", getMappingsRequest.local()));
|
||||
return channel -> client.admin().indices().getMappings(getMappingsRequest, new RestBuilderListener<GetMappingsResponse>(channel) {
|
||||
@Override
|
||||
public RestResponse buildResponse(GetMappingsResponse response, XContentBuilder builder) throws Exception {
|
||||
|
||||
ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> mappingsByIndex = response.getMappings();
|
||||
if (mappingsByIndex.isEmpty()) {
|
||||
if (indices.length != 0 && types.length != 0) {
|
||||
return new BytesRestResponse(OK, builder.startObject().endObject());
|
||||
} else if (indices.length != 0) {
|
||||
public RestResponse buildResponse(final GetMappingsResponse response, final XContentBuilder builder) throws Exception {
|
||||
final ImmutableOpenMap<String, ImmutableOpenMap<String, MappingMetaData>> mappingsByIndex = response.getMappings();
|
||||
if (mappingsByIndex.isEmpty() && (indices.length != 0 || types.length != 0)) {
|
||||
if (indices.length != 0 && types.length == 0) {
|
||||
builder.close();
|
||||
return new BytesRestResponse(channel, new IndexNotFoundException(indices[0]));
|
||||
} else if (types.length != 0) {
|
||||
builder.close();
|
||||
return new BytesRestResponse(channel, new TypeMissingException("_all", types[0]));
|
||||
return new BytesRestResponse(channel, new IndexNotFoundException(String.join(",", indices)));
|
||||
} else {
|
||||
return new BytesRestResponse(OK, builder.startObject().endObject());
|
||||
builder.close();
|
||||
return new BytesRestResponse(channel, new TypeMissingException("_all", String.join(",", types)));
|
||||
}
|
||||
}
|
||||
|
||||
final Set<String> typeNames = new HashSet<>();
|
||||
for (final ObjectCursor<ImmutableOpenMap<String, MappingMetaData>> cursor : mappingsByIndex.values()) {
|
||||
for (final ObjectCursor<String> inner : cursor.value.keys()) {
|
||||
typeNames.add(inner.value);
|
||||
}
|
||||
}
|
||||
|
||||
final SortedSet<String> difference = Sets.sortedDifference(Arrays.stream(types).collect(Collectors.toSet()), typeNames);
|
||||
|
||||
// now remove requested aliases that contain wildcards that are simple matches
|
||||
final List<String> matches = new ArrayList<>();
|
||||
outer:
|
||||
for (final String pattern : difference) {
|
||||
if (pattern.contains("*")) {
|
||||
for (final String typeName : typeNames) {
|
||||
if (Regex.simpleMatch(pattern, typeName)) {
|
||||
matches.add(pattern);
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
difference.removeAll(matches);
|
||||
|
||||
final RestStatus status;
|
||||
builder.startObject();
|
||||
for (ObjectObjectCursor<String, ImmutableOpenMap<String, MappingMetaData>> indexEntry : mappingsByIndex) {
|
||||
builder.startObject(indexEntry.key);
|
||||
builder.startObject(Fields.MAPPINGS);
|
||||
for (ObjectObjectCursor<String, MappingMetaData> typeEntry : indexEntry.value) {
|
||||
builder.field(typeEntry.key);
|
||||
builder.map(typeEntry.value.sourceAsMap());
|
||||
{
|
||||
if (difference.isEmpty()) {
|
||||
status = RestStatus.OK;
|
||||
} else {
|
||||
status = RestStatus.NOT_FOUND;
|
||||
final String message;
|
||||
if (difference.size() == 1) {
|
||||
message = String.format(Locale.ROOT, "type [%s] missing", toNamesString(difference.iterator().next()));
|
||||
} else {
|
||||
message = String.format(Locale.ROOT, "types [%s] missing", toNamesString(difference.toArray(new String[0])));
|
||||
}
|
||||
builder.field("error", message);
|
||||
builder.field("status", status.getStatus());
|
||||
}
|
||||
builder.endObject();
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
for (final ObjectObjectCursor<String, ImmutableOpenMap<String, MappingMetaData>> indexEntry : mappingsByIndex) {
|
||||
builder.startObject(indexEntry.key);
|
||||
{
|
||||
builder.startObject("mappings");
|
||||
{
|
||||
for (final ObjectObjectCursor<String, MappingMetaData> typeEntry : indexEntry.value) {
|
||||
builder.field(typeEntry.key, typeEntry.value.sourceAsMap());
|
||||
}
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
builder.endObject();
|
||||
return new BytesRestResponse(OK, builder);
|
||||
return new BytesRestResponse(status, builder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static class Fields {
|
||||
static final String MAPPINGS = "mappings";
|
||||
private static String toNamesString(final String... names) {
|
||||
return Arrays.stream(names).collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.elasticsearch.rest.action.admin.indices;
|
||||
|
||||
import org.elasticsearch.action.admin.indices.exists.types.TypesExistsRequest;
|
||||
import org.elasticsearch.action.admin.indices.exists.types.TypesExistsResponse;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
import org.elasticsearch.rest.BytesRestResponse;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.RestResponse;
|
||||
import org.elasticsearch.rest.action.RestResponseListener;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.HEAD;
|
||||
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
|
||||
import static org.elasticsearch.rest.RestStatus.OK;
|
||||
|
||||
/**
|
||||
* Rest api for checking if a type exists.
|
||||
*/
|
||||
public class RestTypesExistsAction extends BaseRestHandler {
|
||||
public RestTypesExistsAction(Settings settings, RestController controller) {
|
||||
super(settings);
|
||||
controller.registerWithDeprecatedHandler(
|
||||
HEAD, "/{index}/_mapping/{type}", this,
|
||||
HEAD, "/{index}/{type}", deprecationLogger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "types_exists_action";
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
|
||||
TypesExistsRequest typesExistsRequest = new TypesExistsRequest(
|
||||
Strings.splitStringByCommaToArray(request.param("index")), Strings.splitStringByCommaToArray(request.param("type"))
|
||||
);
|
||||
typesExistsRequest.local(request.paramAsBoolean("local", typesExistsRequest.local()));
|
||||
typesExistsRequest.indicesOptions(IndicesOptions.fromRequest(request, typesExistsRequest.indicesOptions()));
|
||||
return channel -> client.admin().indices().typesExists(typesExistsRequest, new RestResponseListener<TypesExistsResponse>(channel) {
|
||||
@Override
|
||||
public RestResponse buildResponse(TypesExistsResponse response) throws Exception {
|
||||
if (response.isExists()) {
|
||||
return new BytesRestResponse(OK, BytesRestResponse.TEXT_CONTENT_TYPE, BytesArray.EMPTY);
|
||||
} else {
|
||||
return new BytesRestResponse(NOT_FOUND, BytesRestResponse.TEXT_CONTENT_TYPE, BytesArray.EMPTY);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -23,9 +23,9 @@ following are some examples:
|
|||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
GET /_mapping/tweet,kimchy
|
||||
GET /_mapping/tweet
|
||||
|
||||
GET /_all/_mapping/tweet,book
|
||||
GET /_all/_mapping/tweet
|
||||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
// TEST[setup:twitter]
|
||||
|
|
|
@ -76,8 +76,14 @@ public class Netty4HeadBodyIsEmptyIT extends ESRestTestCase {
|
|||
|
||||
public void testTypeExists() throws IOException {
|
||||
createTestDoc();
|
||||
headTestCase("/test/test", emptyMap(), equalTo(0));
|
||||
headTestCase("/test/test", singletonMap("pretty", "true"), equalTo(0));
|
||||
headTestCase("/test/_mapping/test", emptyMap(), greaterThan(0));
|
||||
headTestCase("/test/_mapping/test", singletonMap("pretty", "true"), greaterThan(0));
|
||||
}
|
||||
|
||||
public void testTypeDoesNotExist() throws IOException {
|
||||
createTestDoc();
|
||||
headTestCase("/test/_mapping/does-not-exist", emptyMap(), NOT_FOUND.getStatus(), greaterThan(0));
|
||||
headTestCase("/text/_mapping/test,does-not-exist", emptyMap(), NOT_FOUND.getStatus(), greaterThan(0));
|
||||
}
|
||||
|
||||
public void testAliasExists() throws IOException {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
---
|
||||
"Return empty response when type doesn't exist":
|
||||
"Non-existent type returns 404":
|
||||
- skip:
|
||||
version: " - 5.99.99"
|
||||
reason: Previous versions did not 404 on missing types
|
||||
- do:
|
||||
indices.create:
|
||||
index: test_index
|
||||
|
@ -12,11 +15,91 @@
|
|||
analyzer: whitespace
|
||||
|
||||
- do:
|
||||
catch: missing
|
||||
indices.get_mapping:
|
||||
index: test_index
|
||||
type: not_test_type
|
||||
|
||||
- match: { '': {}}
|
||||
|
||||
- match: { status: 404 }
|
||||
- match: { error.reason: 'type[[not_test_type]] missing' }
|
||||
|
||||
---
|
||||
"No type matching pattern returns 404":
|
||||
- skip:
|
||||
version: " - 5.99.99"
|
||||
reason: Previous versions did not 404 on missing types
|
||||
- do:
|
||||
indices.create:
|
||||
index: test_index
|
||||
body:
|
||||
mappings:
|
||||
test_type:
|
||||
properties:
|
||||
text:
|
||||
type: text
|
||||
analyzer: whitespace
|
||||
|
||||
- do:
|
||||
catch: missing
|
||||
indices.get_mapping:
|
||||
index: test_index
|
||||
type: test*,not*
|
||||
|
||||
- match: { status: 404 }
|
||||
- match: { error: 'type [not*] missing' }
|
||||
- is_true: test_index.mappings.test_type
|
||||
|
||||
---
|
||||
"Existent and non-existent type returns 404 and the existing type":
|
||||
- skip:
|
||||
version: " - 5.99.99"
|
||||
reason: Previous versions did not 404 on missing types
|
||||
- do:
|
||||
indices.create:
|
||||
index: test_index
|
||||
body:
|
||||
mappings:
|
||||
test_type:
|
||||
properties:
|
||||
text:
|
||||
type: text
|
||||
analyzer: whitespace
|
||||
|
||||
- do:
|
||||
catch: missing
|
||||
indices.get_mapping:
|
||||
index: test_index
|
||||
type: test_type,not_test_type
|
||||
|
||||
- match: { status: 404 }
|
||||
- match: { error: 'type [not_test_type] missing' }
|
||||
- is_true: test_index.mappings.test_type
|
||||
|
||||
---
|
||||
"Existent and non-existent types returns 404 and the existing type":
|
||||
- skip:
|
||||
version: " - 5.99.99"
|
||||
reason: Previous versions did not 404 on missing types
|
||||
- do:
|
||||
indices.create:
|
||||
index: test_index
|
||||
body:
|
||||
mappings:
|
||||
test_type:
|
||||
properties:
|
||||
text:
|
||||
type: text
|
||||
analyzer: whitespace
|
||||
|
||||
- do:
|
||||
catch: missing
|
||||
indices.get_mapping:
|
||||
index: test_index
|
||||
type: test_type,not_test_type,another_not_test_type
|
||||
|
||||
- match: { status: 404 }
|
||||
- match: { error: 'types [another_not_test_type,not_test_type] missing' }
|
||||
- is_true: test_index.mappings.test_type
|
||||
|
||||
---
|
||||
"Type missing when no types exist":
|
||||
|
|
Loading…
Reference in New Issue