diff --git a/core/src/main/java/org/elasticsearch/action/delete/DeleteResponse.java b/core/src/main/java/org/elasticsearch/action/delete/DeleteResponse.java index 11d06c166b6..b5a4d74d620 100644 --- a/core/src/main/java/org/elasticsearch/action/delete/DeleteResponse.java +++ b/core/src/main/java/org/elasticsearch/action/delete/DeleteResponse.java @@ -20,12 +20,21 @@ package org.elasticsearch.action.delete; import org.elasticsearch.action.DocWriteResponse; +import org.elasticsearch.action.index.IndexResponse; +import org.elasticsearch.cluster.metadata.IndexMetaData; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ConstructingObjectParser; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.Index; +import org.elasticsearch.index.seqno.SequenceNumbersService; import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.rest.RestStatus; import java.io.IOException; +import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; + /** * The response of the delete action. * @@ -34,6 +43,8 @@ import java.io.IOException; */ public class DeleteResponse extends DocWriteResponse { + private static final String FOUND = "found"; + public DeleteResponse() { } @@ -49,11 +60,35 @@ public class DeleteResponse extends DocWriteResponse { @Override public XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException { - builder.field("found", result == Result.DELETED); + builder.field(FOUND, result == Result.DELETED); super.innerToXContent(builder, params); return builder; } + private static final ConstructingObjectParser PARSER; + static { + PARSER = new ConstructingObjectParser<>(DeleteResponse.class.getName(), + args -> { + // index uuid and shard id are unknown and can't be parsed back for now. + ShardId shardId = new ShardId(new Index((String) args[0], IndexMetaData.INDEX_UUID_NA_VALUE), -1); + String type = (String) args[1]; + String id = (String) args[2]; + long version = (long) args[3]; + ShardInfo shardInfo = (ShardInfo) args[5]; + long seqNo = (args[6] != null) ? (long) args[6] : SequenceNumbersService.UNASSIGNED_SEQ_NO; + boolean found = (boolean) args[7]; + DeleteResponse deleteResponse = new DeleteResponse(shardId, type, id, seqNo, version, found); + deleteResponse.setShardInfo(shardInfo); + return deleteResponse; + }); + DocWriteResponse.declareParserFields(PARSER); + PARSER.declareBoolean(constructorArg(), new ParseField(FOUND)); + } + + public static DeleteResponse fromXContent(XContentParser parser) throws IOException { + return PARSER.apply(parser, null); + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); diff --git a/core/src/test/java/org/elasticsearch/action/delete/DeleteResponseTests.java b/core/src/test/java/org/elasticsearch/action/delete/DeleteResponseTests.java new file mode 100644 index 00000000000..368add2a850 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/action/delete/DeleteResponseTests.java @@ -0,0 +1,105 @@ +/* + * 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.delete; + +import org.elasticsearch.action.DocWriteResponse; +import org.elasticsearch.action.support.replication.ReplicationResponse; +import org.elasticsearch.common.Strings; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.test.ESTestCase; +import org.elasticsearch.test.RandomObjects; + +import java.io.IOException; +import java.util.Map; + +import static org.elasticsearch.action.index.IndexResponseTests.assertDocWriteResponse; +import static org.elasticsearch.common.xcontent.XContentHelper.toXContent; + +public class DeleteResponseTests extends ESTestCase { + + public void testToXContent() throws IOException { + { + DeleteResponse response = new DeleteResponse(new ShardId("index", "index_uuid", 0), "type", "id", 3, 5, true); + String output = Strings.toString(response); + assertEquals("{\"found\":true,\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"_version\":5,\"result\":\"deleted\"," + + "\"_shards\":null,\"_seq_no\":3}", output); + } + { + DeleteResponse response = new DeleteResponse(new ShardId("index", "index_uuid", 0), "type", "id", -1, 7, true); + response.setForcedRefresh(true); + response.setShardInfo(new ReplicationResponse.ShardInfo(10, 5)); + String output = Strings.toString(response); + assertEquals("{\"found\":true,\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"_version\":7,\"result\":\"deleted\"," + + "\"forced_refresh\":true,\"_shards\":{\"total\":10,\"successful\":5,\"failed\":0}}", output); + } + } + + public void testToAndFromXContent() throws IOException { + final XContentType xContentType = randomFrom(XContentType.values()); + + // Create a random DeleteResponse and converts it to XContent in bytes + DeleteResponse deleteResponse = randomDeleteResponse(); + BytesReference deleteResponseBytes = toXContent(deleteResponse, xContentType); + + // Parse the XContent bytes to obtain a parsed + DeleteResponse parsedDeleteResponse; + try (XContentParser parser = createParser(xContentType.xContent(), deleteResponseBytes)) { + parsedDeleteResponse = DeleteResponse.fromXContent(parser); + assertNull(parser.nextToken()); + } + + // We can't use equals() to compare the original and the parsed index response + // because the random index response can contain shard failures with exceptions, + // and those exceptions are not parsed back with the same types. + + // Print the parsed object out and test that the output is the same as the original output + BytesReference parsedDeleteResponseBytes = toXContent(parsedDeleteResponse, xContentType); + try (XContentParser parser = createParser(xContentType.xContent(), parsedDeleteResponseBytes)) { + assertDeleteResponse(deleteResponse, parser.map()); + } + } + + private static void assertDeleteResponse(DeleteResponse expected, Map actual) { + assertDocWriteResponse(expected, actual); + if (expected.getResult() == DocWriteResponse.Result.DELETED) { + assertTrue((boolean) actual.get("found")); + } else { + assertFalse((boolean) actual.get("found")); + } + } + + private static DeleteResponse randomDeleteResponse() { + ShardId shardId = new ShardId(randomAsciiOfLength(5), randomAsciiOfLength(5), randomIntBetween(0, 5)); + String type = randomAsciiOfLength(5); + String id = randomAsciiOfLength(5); + long seqNo = randomIntBetween(-2, 5); + long version = (long) randomIntBetween(0, 5); + boolean found = randomBoolean(); + + DeleteResponse response = new DeleteResponse(shardId, type, id, seqNo, version, found); + response.setForcedRefresh(randomBoolean()); + response.setShardInfo(RandomObjects.randomShardInfo(random(), randomBoolean())); + return response; + } + +}