-
Notifications
You must be signed in to change notification settings - Fork 25k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add parsing methods for UpdateResponse #22586
Add parsing methods for UpdateResponse #22586
Conversation
This commit adds the fromXContent() method to the UpdateResponse class, so that it can be used with the high level rest client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
left a few small comments, LGTM otherwise
@@ -292,8 +292,8 @@ protected static void declareParserFields(ConstructingObjectParser<? extends Doc | |||
objParser.declareString(constructorArg(), new ParseField(_ID)); | |||
objParser.declareLong(constructorArg(), new ParseField(_VERSION)); | |||
objParser.declareString(constructorArg(), new ParseField(RESULT)); | |||
objParser.declareObject(constructorArg(), (p, c) -> ShardInfo.fromXContent(p), new ParseField(_SHARDS)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
did ShardInfo become a constructor argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No - I changed it to optionalConstructorArg()
since it can be an argument of the UpdateResponse
.
* 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. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can remove this comment ;) no need to explain why you are using ObjectParser I think :)
try (XContentBuilder builder = XContentBuilder.builder(xContent)) { | ||
// "get" field contains an embedded version of {@link GetResult} and requires | ||
// to be parsed as if it was a full XContent object. | ||
XContentHelper.copyCurrentStructure(builder.generator(), p); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are we copying this? because the xContentType can differ between the response and the get section?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same question
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because the xContentType can differ between the response and the get section?
No, this has been a crappy hack around the fact that GetResult
can be rendered as a ToXContentObject
using toXContent()
or as a fragment using toXContentEmbedded()
.
I changed that so that GetResult
now has a fromXContentEmbedded()
parsing method that can be used in UpdateResponse's object parser.
Note that GetResult.toXContent()
delegates all the work to fromXContentEmbedded()
. Please let me know what you think about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
++ thanks
|
||
// We can't use equals() to compare the original and the parsed update response | ||
// because the random update response can contain shard failures with exceptions, | ||
// and those exceptions are not parsed back with the same types. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we rather generate responses without exceptions and test them like we usually do, and test exceptions separately?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed that part to do something slightly different. We can't compare UpdateResponse
using equals()
because it can have shard failures AND the GetResult
object embedded in the update response is a bit different after parsing (it does not have index/type/id/version).
For the shard failures, we already have IndexResponseTests.assertDocWriteResponse()
which takes care of checking shard failures, so I reused it.
For the GetResult, I used something similar to the GetResultTests
where it generates and returns a Tuple of actual and expected GetResult
, and I set to null the index/type/etc values expected for the parsed GetResult.
} | ||
|
||
private static void assertUpdateResponse(UpdateResponse expected, Map<String, Object> actual) { | ||
IndexResponseTests.assertDocWriteResponse(expected, actual); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't we have to check also the get result part?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, of course, how can I forgot that? It's like running a 100 meters and forget to pass the finish line...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also took a look but other than the question @javanna already asked I have nothing to add at the moment. Will take another final look if necessary.
@javanna @cbuescher I updated the pull request with your comments. I think I addressed all of them. It would be great if you can have another look. |
assertNull(parser.nextToken()); | ||
} | ||
|
||
final UpdateResponse updateResponse = tuple.v2(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you call the variable expected or something along those lines?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed to expectedUpdateResponse
@@ -160,4 +213,13 @@ public static GetResult mutateGetResult(GetResult getResult) { | |||
} | |||
return Tuple.tuple(fields, expectedFields); | |||
} | |||
|
|||
private static BytesReference toXContentEmbedded(GetResult getResult, XContentType xContentType) throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should you rather have an anonymous ToXContent that delegates GetResult#toXContentEmbedded and call XContentHelper#toXContent instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
long version = actualGetResult.getVersion(); | ||
DocWriteResponse.Result result = actualGetResult.isExists() ? DocWriteResponse.Result.UPDATED : DocWriteResponse.Result.NOT_FOUND; | ||
|
||
Long seqNo = randomBoolean() ? null : randomNonNegativeLong(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generating random longs like this tends to generate really large numbers. I added another 50/50 condition in some of my randomized tests that do e.g. a %10000 to get smaller numbers here more often, this uncovered some type conversion issues (integer/long) in the parser. Maybe you could add something like that here too, since smaller numbers are more likely to appear in real responses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
++ let's add it as a common method somewhere so we remember to do it in other places too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I think a common methods would be great, I'm not sure this distinction (small vs. large longs) is needed very often. In the parsing tests its good to have some more "smaller" values to catch cases where they might be parsed back as int. I wonder how a common method would be called (randomNonNegativeLongButOftenSmall?), which range to sample the small values from (that might vary between tests) and where to put it. But maybe @tlrx has some suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I changed to Long seqNo = randomFrom(randomNonNegativeLong(), (long) randomIntBetween(0, 10_000), null);
and I also added a comment.
I'm for having less trivial random methods, I really prefer to repeat stuff and adds comments where it makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
++ I like that solution here
}); | ||
|
||
DocWriteResponse.declareParserFields(PARSER); | ||
PARSER.declareObject(UpdateResponse::setGetResult, (p, c) -> GetResult.fromXContentEmbedded(p), new ParseField(GET)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might be possible to use the index/type/id information from the already constructed UpdateResponse here to set those missing fields for the GetResult. It looks a bit odd but I think it might work, will need to check that in test though. Or can the index/type/id in the get result be different from the values in the overall response?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do sth. like this I think instead of directly setting the parsed (incomplete GetResult):
(response, getResult) -> response.setGetResult(new GetResult(response.getIndex(), response.getType(), response.getId(), getResult.getVersion(), getResult.isExists(), getResult.internalSourceRef(), getResult.getFields()))
This way you can use the index/type/id from the parent UpdateResponse together with the rest of the parsed GetResult information. I haven't tested this very thoroughly though because didn't completely understand the test setup yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had the same idea but at that time all the object was parsed in the ConstructingObjectParser
. Then I reverted this part and added a comment in the randomUpdateResponse()
test method, but now I'm thinking of it again it does not cost a lot and it makes sense. Thanks for raising this point again.
PARSER.declareObject(UpdateResponse::setGetResult, (p, c) -> GetResult.fromXContentEmbedded(p), new ParseField(GET)); | ||
} | ||
|
||
public static UpdateResponse fromXContent(XContentParser parser) throws IOException { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: IOException is not thrown
// by the toXContentEmbedded method. | ||
GetResult expectedGetResult = new GetResult(null, null, null, -1, | ||
getResults.v2().isExists(), getResults.v2().sourceRef(), getResults.v2().getFields()); | ||
expected.setGetResult(expectedGetResult); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the expected get result only set to the expected UpdateResponse when seqNo is not null? Should this be behind the if block instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, it should be aligned with actualGetResult.isExists() / expectedGetResult.isExists(). I'll change that.
|
||
public void testToAndFromXContent() throws IOException { | ||
final XContentType xContentType = randomFrom(XContentType.values()); | ||
final Tuple<UpdateResponse, UpdateResponse> tuple = randomUpdateResponse(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a problem with this test setup that I just found: the xContentType that is used for parsing here is not necessarily the same as the one that is used int randomUpdateResponse(). So the expected values might be off, e.g. if in randomUpdateResponse() SMILE is used and here xContentType is Yaml.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are right thanks a lot for catching this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch @cbuescher ! Vielen dank!
ReplicationResponse.ShardInfo shardInfo = RandomObjects.randomShardInfo(random(), true); | ||
|
||
actual = new UpdateResponse(shardInfo, shardId, type, id, seqNo, version, result); | ||
actual.setGetResult(actualGetResult); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here, seems like the getResult should be set regardless of seqNo being present or not, or do I miss something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, thanks
@tlrx I left some more comments concerning parsing the GetResult. I think this would simplify the whole test a little bit. |
@cbuescher @javanna Thanks! I think I addressed all your comments in the last two commits. Are we good to go? |
++ on my end |
for me also, LGTM |
Thanks @cbuescher @javanna ! |
This commit adds the fromXContent() method to the UpdateResponse class, so that it can be used with the high level rest client.
* master: (117 commits) Add missing serialization BWC for disk usage estimates Expose disk usage estimates in nodes stats S3 repository: Deprecate specifying credentials through env vars, sys props, and remove profile files (elastic#22567) Fix Eclipse project generation Fix deprecation logging for lenient booleans Remove @Header we no longer need Make lexer abstract [Docs] Remove outdated info about enabling/disabling doc_values (elastic#22694) Move lexer hacks to EnhancedPainlessLexer Fix incorrect args order passed to createAggregator Improve painless's javadocs Add TestWithDependenciesPlugin to build (elastic#22646) Add parsing from xContent to SearchProfileShardResults and nested classes (elastic#22649) Add unit tests for FiltersAggregator (elastic#22678) Don't register search response listener in transport clients unmute FieldStatsIntegrationIT.testGeoPointNotIndexed, fix already pushed Mute FieldStatsIntegrationIT.testGeoPointNotIndexed, for now Painless: Add augmentation to string for base 64 (elastic#22665) Fix NPE on FieldStats with mixed cluster on version pre/post 5.2 (elastic#22688) Add parsing methods for UpdateResponse (elastic#22586) ...
This commit adds the
fromXContent()
method to theUpdateResponse
class, so that it can be used with the high level rest client.