Add fromxcontent methods to index response (#22229)

This commit adds the parsing fromXContent() methods to the IndexResponse class. The method is based on a ObjectParser because it is easier to use when parsing parent abstract classes like DocWriteResponse.

It also changes the ReplicationResponse.ShardInfo so that it now implements ToXContentObject. This way, the ShardInfo.fromXContent() method can be used by the IndexResponse's ObjectParser.
This commit is contained in:
Tanguy Leroux 2017-01-10 20:25:32 +01:00 committed by GitHub
parent d6a23de002
commit 2dcb05fca8
7 changed files with 315 additions and 76 deletions

View File

@ -24,9 +24,12 @@ import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
import org.elasticsearch.action.support.WriteResponse;
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.StatusToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.IndexSettings;
@ -39,11 +42,23 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.util.Locale;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
/**
* A base class for the response of a write operation that involves a single doc
*/
public abstract class DocWriteResponse extends ReplicationResponse implements WriteResponse, StatusToXContentObject {
private static final String _SHARDS = "_shards";
private static final String _INDEX = "_index";
private static final String _TYPE = "_type";
private static final String _ID = "_id";
private static final String _VERSION = "_version";
private static final String _SEQ_NO = "_seq_no";
private static final String RESULT = "result";
private static final String FORCED_REFRESH = "forced_refresh";
/**
* An enum that represents the the results of CRUD operations, primarily used to communicate the type of
* operation that occurred.
@ -253,18 +268,32 @@ public abstract class DocWriteResponse extends ReplicationResponse implements Wr
public XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException {
ReplicationResponse.ShardInfo shardInfo = getShardInfo();
builder.field("_index", shardId.getIndexName())
.field("_type", type)
.field("_id", id)
.field("_version", version)
.field("result", getResult().getLowercase());
builder.field(_INDEX, shardId.getIndexName())
.field(_TYPE, type)
.field(_ID, id)
.field(_VERSION, version)
.field(RESULT, getResult().getLowercase());
if (forcedRefresh) {
builder.field("forced_refresh", true);
builder.field(FORCED_REFRESH, true);
}
shardInfo.toXContent(builder, params);
builder.field(_SHARDS, shardInfo);
if (getSeqNo() >= 0) {
builder.field("_seq_no", getSeqNo());
builder.field(_SEQ_NO, getSeqNo());
}
return builder;
}
/**
* Declare the {@link ObjectParser} fields to use when parsing a {@link DocWriteResponse}
*/
protected static void declareParserFields(ConstructingObjectParser<? extends DocWriteResponse, Void> objParser) {
objParser.declareString(constructorArg(), new ParseField(_INDEX));
objParser.declareString(constructorArg(), new ParseField(_TYPE));
objParser.declareString(constructorArg(), new ParseField(_ID));
objParser.declareLong(constructorArg(), new ParseField(_VERSION));
objParser.declareString(constructorArg(), new ParseField(RESULT));
objParser.declareLong(optionalConstructorArg(), new ParseField(_SEQ_NO));
objParser.declareBoolean(DocWriteResponse::setForcedRefresh, new ParseField(FORCED_REFRESH));
objParser.declareObject(DocWriteResponse::setShardInfo, (p, c) -> ShardInfo.fromXContent(p), new ParseField(_SHARDS));
}
}

View File

@ -20,13 +20,21 @@
package org.elasticsearch.action.index;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
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;
/**
* A response of an index operation,
*
@ -35,6 +43,8 @@ import java.io.IOException;
*/
public class IndexResponse extends DocWriteResponse {
private static final String CREATED = "created";
public IndexResponse() {
}
@ -64,7 +74,34 @@ public class IndexResponse extends DocWriteResponse {
@Override
public XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException {
super.innerToXContent(builder, params);
builder.field("created", result == Result.CREATED);
builder.field(CREATED, result == Result.CREATED);
return builder;
}
/**
* ConstructingObjectParser used to parse the {@link IndexResponse}. We use a ObjectParser here
* because most fields are parsed by the parent abstract class {@link DocWriteResponse} and it's
* not easy to parse part of the fields in the parent class and other fields in the children class
* using the usual streamed parsing method.
*/
private static final ConstructingObjectParser<IndexResponse, Void> PARSER;
static {
PARSER = new ConstructingObjectParser<>(IndexResponse.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];
long seqNo = (args[5] != null) ? (long) args[5] : SequenceNumbersService.UNASSIGNED_SEQ_NO;
boolean created = (boolean) args[6];
return new IndexResponse(shardId, type, id, seqNo, version, created);
});
DocWriteResponse.declareParserFields(PARSER);
PARSER.declareBoolean(constructorArg(), new ParseField(CREATED));
}
public static IndexResponse fromXContent(XContentParser parser) throws IOException {
return PARSER.apply(parser, null);
}
}

View File

@ -73,7 +73,7 @@ public class ReplicationResponse extends ActionResponse {
this.shardInfo = shardInfo;
}
public static class ShardInfo implements Streamable, ToXContent {
public static class ShardInfo implements Streamable, ToXContentObject {
private static final String _SHARDS = "_shards";
private static final String TOTAL = "total";
@ -179,7 +179,7 @@ public class ReplicationResponse extends ActionResponse {
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(_SHARDS);
builder.startObject();
builder.field(TOTAL, total);
builder.field(SUCCESSFUL, successful);
builder.field(FAILED, getFailed());
@ -195,18 +195,12 @@ public class ReplicationResponse extends ActionResponse {
}
public static ShardInfo fromXContent(XContentParser parser) throws IOException {
XContentParser.Token token = parser.nextToken();
ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser::getTokenLocation);
String currentFieldName = parser.currentName();
if (_SHARDS.equals(currentFieldName) == false) {
throwUnknownField(currentFieldName, parser.getTokenLocation());
}
token = parser.nextToken();
XContentParser.Token token = parser.currentToken();
ensureExpectedToken(XContentParser.Token.START_OBJECT, token, parser::getTokenLocation);
int total = 0, successful = 0;
List<Failure> failuresList = null;
String currentFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();

View File

@ -147,7 +147,7 @@ public class IndexRequestTests extends ESTestCase {
assertEquals("IndexResponse[index=" + shardId.getIndexName() + ",type=" + type + ",id="+ id +
",version=" + version + ",result=" + (created ? "created" : "updated") +
",seqNo=" + SequenceNumbersService.UNASSIGNED_SEQ_NO +
",shards={\"_shards\":{\"total\":" + total + ",\"successful\":" + successful + ",\"failed\":0}}]",
",shards={\"total\":" + total + ",\"successful\":" + successful + ",\"failed\":0}]",
indexResponse.toString());
}
}

View File

@ -0,0 +1,168 @@
/*
* 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.index;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.util.CollectionUtils;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.RandomObjects;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.common.xcontent.XContentHelper.toXContent;
public class IndexResponseTests extends ESTestCase {
public void testToXContent() throws IOException {
{
IndexResponse indexResponse = new IndexResponse(new ShardId("index", "index_uuid", 0), "type", "id", 3, 5, true);
String output = Strings.toString(indexResponse);
assertEquals("{\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"_version\":5,\"result\":\"created\",\"_shards\":null," +
"\"_seq_no\":3,\"created\":true}", output);
}
{
IndexResponse indexResponse = new IndexResponse(new ShardId("index", "index_uuid", 0), "type", "id", -1, 7, true);
indexResponse.setForcedRefresh(true);
indexResponse.setShardInfo(new ReplicationResponse.ShardInfo(10, 5));
String output = Strings.toString(indexResponse);
assertEquals("{\"_index\":\"index\",\"_type\":\"type\",\"_id\":\"id\",\"_version\":7,\"result\":\"created\"," +
"\"forced_refresh\":true,\"_shards\":{\"total\":10,\"successful\":5,\"failed\":0},\"created\":true}", output);
}
}
public void testToAndFromXContent() throws IOException {
final XContentType xContentType = randomFrom(XContentType.values());
// Create a random IndexResponse and converts it to XContent in bytes
IndexResponse indexResponse = randomIndexResponse();
BytesReference indexResponseBytes = toXContent(indexResponse, xContentType);
// Parse the XContent bytes to obtain a parsed
IndexResponse parsedIndexResponse;
try (XContentParser parser = createParser(xContentType.xContent(), indexResponseBytes)) {
parsedIndexResponse = IndexResponse.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 parsedIndexResponseBytes = toXContent(parsedIndexResponse, xContentType);
try (XContentParser parser = createParser(xContentType.xContent(), parsedIndexResponseBytes)) {
assertIndexResponse(indexResponse, parser.map());
}
}
private static void assertIndexResponse(IndexResponse expected, Map<String, Object> actual) {
assertEquals(expected.getIndex(), actual.get("_index"));
assertEquals(expected.getType(), actual.get("_type"));
assertEquals(expected.getId(), actual.get("_id"));
assertEquals(expected.getVersion(), ((Integer) actual.get("_version")).longValue());
assertEquals(expected.getResult().getLowercase(), actual.get("result"));
if (expected.forcedRefresh()) {
assertTrue((Boolean) actual.get("forced_refresh"));
} else {
assertFalse(actual.containsKey("forced_refresh"));
}
if (expected.getSeqNo() >= 0) {
assertEquals(expected.getSeqNo(), ((Integer) actual.get("_seq_no")).longValue());
} else {
assertFalse(actual.containsKey("_seq_no"));
}
Map<String, Object> actualShards = (Map<String, Object>) actual.get("_shards");
assertNotNull(actualShards);
assertEquals(expected.getShardInfo().getTotal(), actualShards.get("total"));
assertEquals(expected.getShardInfo().getSuccessful(), actualShards.get("successful"));
assertEquals(expected.getShardInfo().getFailed(), actualShards.get("failed"));
List<Map<String, Object>> actualFailures = (List<Map<String, Object>>) actualShards.get("failures");
if (CollectionUtils.isEmpty(expected.getShardInfo().getFailures())) {
assertNull(actualFailures);
} else {
assertEquals(expected.getShardInfo().getFailures().length, actualFailures.size());
for (int i = 0; i < expected.getShardInfo().getFailures().length; i++) {
ReplicationResponse.ShardInfo.Failure failure = expected.getShardInfo().getFailures()[i];
Map<String, Object> actualFailure = actualFailures.get(i);
assertEquals(failure.index(), actualFailure.get("_index"));
assertEquals(failure.shardId(), actualFailure.get("_shard"));
assertEquals(failure.nodeId(), actualFailure.get("_node"));
assertEquals(failure.status(), RestStatus.valueOf((String) actualFailure.get("status")));
assertEquals(failure.primary(), actualFailure.get("primary"));
Throwable cause = failure.getCause();
Map<String, Object> actualClause = (Map<String, Object>) actualFailure.get("reason");
assertNotNull(actualClause);
while (cause != null) {
// The expected IndexResponse has been converted in XContent, then the resulting bytes have been
// parsed to create a new parsed IndexResponse. During this process, the type of the exceptions
// have been lost.
assertEquals("exception", actualClause.get("type"));
String expectedMessage = "Elasticsearch exception [type=" + ElasticsearchException.getExceptionName(cause)
+ ", reason=" + cause.getMessage() + "]";
assertEquals(expectedMessage, actualClause.get("reason"));
if (cause instanceof ElasticsearchException) {
ElasticsearchException ex = (ElasticsearchException) cause;
Map<String, Object> actualHeaders = (Map<String, Object>) actualClause.get("header");
// When a IndexResponse is converted to XContent, the exception headers that start with "es."
// are added to the XContent as fields with the prefix removed. Other headers are added under
// a "header" root object.
// In the test, the "es." prefix is lost when the XContent is generating, so when the parsed
// IndexResponse is converted back to XContent all exception headers are under the "header" object.
for (String name : ex.getHeaderKeys()) {
assertEquals(ex.getHeader(name).get(0), actualHeaders.get(name.replaceFirst("es.", "")));
}
}
actualClause = (Map<String, Object>) actualClause.get("caused_by");
cause = cause.getCause();
}
}
}
}
private static IndexResponse randomIndexResponse() {
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 created = randomBoolean();
IndexResponse indexResponse = new IndexResponse(shardId, type, id, seqNo, version, created);
indexResponse.setForcedRefresh(randomBoolean());
indexResponse.setShardInfo(RandomObjects.randomShardInfo(random(), randomBoolean()));
return indexResponse;
}
}

View File

@ -20,17 +20,17 @@
package org.elasticsearch.action.support.replication;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.RoutingMissingException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.shard.IndexShardRecoveringException;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.EqualsHashCodeTestUtils;
import org.elasticsearch.test.RandomObjects;
import java.io.IOException;
import java.util.ArrayList;
@ -42,6 +42,7 @@ import java.util.Set;
import java.util.function.Supplier;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.hamcrest.Matchers.instanceOf;
public class ReplicationResponseTests extends ESTestCase {
@ -64,12 +65,13 @@ public class ReplicationResponseTests extends ESTestCase {
new ReplicationResponse.ShardInfo(shardInfo.getTotal(), shardInfo.getSuccessful() + 1, shardInfo.getFailures()));
mutations.add(() -> {
int nbFailures = randomIntBetween(1, 5);
return new ReplicationResponse.ShardInfo(shardInfo.getTotal(), shardInfo.getSuccessful(), randomFailures(nbFailures));
ReplicationResponse.ShardInfo.Failure[] randomFailures = RandomObjects.randomShardInfoFailures(random(), nbFailures);
return new ReplicationResponse.ShardInfo(shardInfo.getTotal(), shardInfo.getSuccessful(), randomFailures);
});
return randomFrom(mutations).get();
};
checkEqualsAndHashCode(randomShardInfo(), copy, mutate);
checkEqualsAndHashCode(RandomObjects.randomShardInfo(random(), randomBoolean()), copy, mutate);
}
public void testFailureEqualsAndHashcode() {
@ -127,7 +129,7 @@ public class ReplicationResponseTests extends ESTestCase {
return randomFrom(mutations).get();
};
checkEqualsAndHashCode(randomFailure(), copy, mutate);
checkEqualsAndHashCode(RandomObjects.randomShardInfoFailure(random()), copy, mutate);
}
public void testShardInfoToXContent() throws IOException {
@ -136,11 +138,9 @@ public class ReplicationResponseTests extends ESTestCase {
final ReplicationResponse.ShardInfo shardInfo = new ReplicationResponse.ShardInfo(5, 3);
final BytesReference shardInfoBytes = XContentHelper.toXContent(shardInfo, xContentType);
// Expected JSON is {"_shards":{"total":5,"successful":3,"failed":0}}
// Expected JSON is {"total":5,"successful":3,"failed":0}
assertThat(shardInfo, instanceOf(ToXContentObject.class));
try (XContentParser parser = createParser(xContentType.xContent(), shardInfoBytes)) {
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken());
assertEquals("_shards", parser.currentName());
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken());
assertEquals("total", parser.currentName());
@ -155,7 +155,6 @@ public class ReplicationResponseTests extends ESTestCase {
assertEquals(XContentParser.Token.VALUE_NUMBER, parser.nextToken());
assertEquals(shardInfo.getFailed(), parser.intValue());
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
assertNull(parser.nextToken());
}
}
@ -168,10 +167,9 @@ public class ReplicationResponseTests extends ESTestCase {
ReplicationResponse.ShardInfo parsedShardInfo;
try (XContentParser parser = createParser(xContentType.xContent(), shardInfoBytes)) {
// Move to the start object that was manually added when building the object
// Move to the first start object
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
parsedShardInfo = ReplicationResponse.ShardInfo.fromXContent(parser);
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
assertNull(parser.nextToken());
}
// We can use assertEquals because the shardInfo doesn't have a failure (and exceptions)
@ -184,13 +182,10 @@ public class ReplicationResponseTests extends ESTestCase {
public void testShardInfoWithFailureToXContent() throws IOException {
final XContentType xContentType = randomFrom(XContentType.values());
final ReplicationResponse.ShardInfo shardInfo = randomShardInfo();
final ReplicationResponse.ShardInfo shardInfo = RandomObjects.randomShardInfo(random(), true);
final BytesReference shardInfoBytes = XContentHelper.toXContent(shardInfo, xContentType);
try (XContentParser parser = createParser(xContentType.xContent(), shardInfoBytes)) {
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken());
assertEquals("_shards", parser.currentName());
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
assertEquals(XContentParser.Token.FIELD_NAME, parser.nextToken());
assertEquals("total", parser.currentName());
@ -216,7 +211,6 @@ public class ReplicationResponseTests extends ESTestCase {
assertEquals(XContentParser.Token.END_ARRAY, parser.nextToken());
}
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
assertNull(parser.nextToken());
}
@ -225,15 +219,14 @@ public class ReplicationResponseTests extends ESTestCase {
public void testRandomShardInfoFromXContent() throws IOException {
final XContentType xContentType = randomFrom(XContentType.values());
final ReplicationResponse.ShardInfo shardInfo = randomShardInfo();
final ReplicationResponse.ShardInfo shardInfo = RandomObjects.randomShardInfo(random(), randomBoolean());
final BytesReference shardInfoBytes = XContentHelper.toXContent(shardInfo, xContentType);
ReplicationResponse.ShardInfo parsedShardInfo;
try (XContentParser parser = createParser(xContentType.xContent(), shardInfoBytes)) {
// Move to the start object that was manually added when building the object
// Move to the first start object
assertEquals(XContentParser.Token.START_OBJECT, parser.nextToken());
parsedShardInfo = ReplicationResponse.ShardInfo.fromXContent(parser);
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
assertNull(parser.nextToken());
}
@ -266,7 +259,7 @@ public class ReplicationResponseTests extends ESTestCase {
public void testRandomFailureToXContent() throws IOException {
final XContentType xContentType = randomFrom(XContentType.values());
final ReplicationResponse.ShardInfo.Failure shardInfoFailure = randomFailure();
final ReplicationResponse.ShardInfo.Failure shardInfoFailure = RandomObjects.randomShardInfoFailure(random());
final BytesReference shardInfoBytes = XContentHelper.toXContent(shardInfoFailure, xContentType);
try (XContentParser parser = createParser(xContentType.xContent(), shardInfoBytes)) {
@ -277,7 +270,7 @@ public class ReplicationResponseTests extends ESTestCase {
public void testRandomFailureToAndFromXContent() throws IOException {
final XContentType xContentType = randomFrom(XContentType.values());
final ReplicationResponse.ShardInfo.Failure shardInfoFailure = randomFailure();
final ReplicationResponse.ShardInfo.Failure shardInfoFailure = RandomObjects.randomShardInfoFailure(random());;
final BytesReference shardInfoBytes = XContentHelper.toXContent(shardInfoFailure, xContentType);
ReplicationResponse.ShardInfo.Failure parsedFailure;
@ -358,32 +351,4 @@ public class ReplicationResponseTests extends ESTestCase {
}
assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken());
}
private static ReplicationResponse.ShardInfo randomShardInfo() {
int total = randomIntBetween(1, 10);
int successful = randomIntBetween(0, total);
return new ReplicationResponse.ShardInfo(total, successful, randomFailures(Math.max(0, (total - successful))));
}
private static ReplicationResponse.ShardInfo.Failure[] randomFailures(int nbFailures) {
List<ReplicationResponse.ShardInfo.Failure> randomFailures = new ArrayList<>(nbFailures);
for (int i = 0; i < nbFailures; i++) {
randomFailures.add(randomFailure());
}
return randomFailures.toArray(new ReplicationResponse.ShardInfo.Failure[nbFailures]);
}
private static ReplicationResponse.ShardInfo.Failure randomFailure() {
return new ReplicationResponse.ShardInfo.Failure(
new ShardId(randomAsciiOfLength(5), randomAsciiOfLength(5), randomIntBetween(0, 5)),
randomAsciiOfLength(3),
randomFrom(
new IndexShardRecoveringException(new ShardId("_test", "_0", 5)),
new ElasticsearchException(new IllegalArgumentException("argument is wrong")),
new RoutingMissingException("_test", "_type", "_id")
),
randomFrom(RestStatus.values()),
randomBoolean()
);
}
}

View File

@ -19,9 +19,11 @@
package org.elasticsearch.test;
import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import com.carrotsearch.randomizedtesting.generators.RandomStrings;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.RoutingMissingException;
import org.elasticsearch.action.support.replication.ReplicationResponse.ShardInfo;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple;
@ -29,15 +31,22 @@ import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.shard.IndexShardRecoveringException;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.rest.RestStatus;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Random;
import static com.carrotsearch.randomizedtesting.generators.RandomNumbers.randomIntBetween;
import static com.carrotsearch.randomizedtesting.generators.RandomStrings.randomAsciiOfLength;
import static com.carrotsearch.randomizedtesting.generators.RandomStrings.randomUnicodeOfLengthBetween;
import static org.elasticsearch.test.ESTestCase.randomFrom;
public final class RandomObjects {
@ -56,10 +65,10 @@ public final class RandomObjects {
* @param xContentType the content type, used to determine what the expected values are for float numbers.
*/
public static Tuple<List<Object>, List<Object>> randomStoredFieldValues(Random random, XContentType xContentType) {
int numValues = RandomNumbers.randomIntBetween(random, 1, 5);
int numValues = randomIntBetween(random, 1, 5);
List<Object> originalValues = new ArrayList<>();
List<Object> expectedParsedValues = new ArrayList<>();
int dataType = RandomNumbers.randomIntBetween(random, 0, 8);
int dataType = randomIntBetween(random, 0, 8);
for (int i = 0; i < numValues; i++) {
switch(dataType) {
case 0:
@ -153,7 +162,7 @@ public final class RandomObjects {
* Randomly adds fields, objects, or arrays to the provided builder. The maximum depth is 5.
*/
private static void addFields(Random random, XContentBuilder builder, int currentDepth) throws IOException {
int numFields = RandomNumbers.randomIntBetween(random, 1, 5);
int numFields = randomIntBetween(random, 1, 5);
for (int i = 0; i < numFields; i++) {
if (currentDepth < 5 && random.nextBoolean()) {
if (random.nextBoolean()) {
@ -162,7 +171,7 @@ public final class RandomObjects {
builder.endObject();
} else {
builder.startArray(RandomStrings.randomAsciiOfLengthBetween(random, 3, 10));
int numElements = RandomNumbers.randomIntBetween(random, 1, 5);
int numElements = randomIntBetween(random, 1, 5);
boolean object = random.nextBoolean();
int dataType = -1;
if (object == false) {
@ -187,7 +196,7 @@ public final class RandomObjects {
}
private static int randomDataType(Random random) {
return RandomNumbers.randomIntBetween(random, 0, 3);
return randomIntBetween(random, 0, 3);
}
private static Object randomFieldValue(Random random, int dataType) {
@ -204,4 +213,41 @@ public final class RandomObjects {
throw new UnsupportedOperationException();
}
}
/**
* Returns a random {@link ShardInfo} object with on or more {@link ShardInfo.Failure} if requested.
*
* @param random Random generator
* @param failures If true, the {@link ShardInfo} will have random failures
* @return a random {@link ShardInfo}
*/
public static ShardInfo randomShardInfo(Random random, boolean failures) {
int total = randomIntBetween(random, 1, 10);
if (failures == false) {
return new ShardInfo(total, total);
}
int successful = randomIntBetween(random, 1, total);
return new ShardInfo(total, successful, randomShardInfoFailures(random, Math.max(1, (total - successful))));
}
public static ShardInfo.Failure[] randomShardInfoFailures(Random random, int nbFailures) {
List<ShardInfo.Failure> randomFailures = new ArrayList<>(nbFailures);
for (int i = 0; i < nbFailures; i++) {
randomFailures.add(randomShardInfoFailure(random));
}
return randomFailures.toArray(new ShardInfo.Failure[nbFailures]);
}
public static ShardInfo.Failure randomShardInfoFailure(Random random) {
String index = randomAsciiOfLength(random, 5);
String indexUuid = randomAsciiOfLength(random, 5);
int shardId = randomIntBetween(random, 1, 10);
ShardId shard = new ShardId(index, indexUuid, shardId);
RestStatus restStatus = randomFrom(RestStatus.values());
Exception exception = RandomPicks.randomFrom(random, Arrays.asList(new IndexShardRecoveringException(shard),
new ElasticsearchException(new IllegalArgumentException("Argument is wrong")),
new RoutingMissingException(index, randomAsciiOfLength(random, 5), randomAsciiOfLength(random, 5))));
return new ShardInfo.Failure(shard, randomAsciiOfLength(random, 3), exception, restStatus, random.nextBoolean());
}
}