-
Notifications
You must be signed in to change notification settings - Fork 24.9k
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
Make yaml test runner stricter by enforcing required
for paths and parameters
#27035
Conversation
Since this is a community submitted pull request, a Jenkins build has not been kicked off automatically. Can an Elastic organization member please verify the contents of this patch and then kick off a build manually? |
@elasticmachine ok to test |
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 for making this change!
I left a few minor nick picky things. The biggest one is error handling in the parsing. I know we haven't been great about error handling in the yaml parsing of this stuff before but I'd like us to be better.
@@ -80,19 +83,33 @@ public ClientYamlTestResponse callApi(String apiName, Map<String, String> params | |||
//divide params between ones that go within query string and ones that go within path | |||
Map<String, String> pathParts = new HashMap<>(); | |||
Map<String, String> queryStringParams = new HashMap<>(); | |||
|
|||
List<String> apiPathParts = restApi.getPathParts().entrySet().stream().filter(e -> e.getValue() == true).map(Entry::getKey) |
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.
Maybe Set<String>
instead? The list should be short so it likely doesn't matter but it feels better to me if it is a set.
return pathParts; | ||
} | ||
|
||
void addPathPart(String pathPart) { | ||
this.pathParts.add(pathPart); | ||
void addPathPart(String pathPart, Boolean required) { |
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 should be bool
rather than Boolean
.
@@ -98,20 +99,20 @@ void addPath(String path) { | |||
this.paths.add(path); | |||
} | |||
|
|||
public List<String> getPathParts() { | |||
public Map<String, Boolean> getPathParts() { |
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.
Probably should have javadoc because it isn't obvious what the boolean reads unless you read it as part of this PR.
if ("required".equals(parser.currentName())) { | ||
parser.nextToken(); | ||
required = parser.booleanValue(); | ||
} |
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 either replace this with CosntructingObjectParser<Boolean>
or manually throw IOException
s for things other than required
and for tokens other than FIELD_NAME
?
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.
@nik9000 thanks for the hint! (I did not know of this parser)
I think that CosntructingObjectParser<Boolean>
cannot be used here as the required
field is an optional one.
I couldn't find an example how to use ConstructingObjectParser
for the primitive wrapper classes. Do you have one?
assertThat(restApi.getPathParts().get(1), equalTo("index")); | ||
assertThat(restApi.getPathParts().get(2), equalTo("type")); | ||
assertThat(restApi.getPathParts().keySet(), containsInAnyOrder("id","index", "type")); | ||
assertThat(restApi.getPathParts().get("index"), equalTo(true)); |
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.
In cases like this I find that
assertThat(restApi.getPathParts(), hasEntry("index", true));
produces more readable error messages.
@nik9000 thanks for the review! Can you have another look? |
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 agree about the object parser being a bit painful here. But it does get you all that error checking for free which is pretty great. I think you've got it going the right direction though.
I left a minor thing. Otherwise I think this is good to go!
} | ||
|
||
private boolean getRequired(XContentParser parser) throws IOException { | ||
ObjectParser<YamlElement, Void> objParser = new ObjectParser<>("", true, YamlElement::new); |
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.
We tend to make these static.
@@ -146,4 +147,20 @@ public ClientYamlSuiteRestApi parse(String location, XContentParser parser) thro | |||
|
|||
return restApi; | |||
} | |||
|
|||
class YamlElement { |
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'd be more reasonable to name this Parameter
or something.
@nik9000 ready for another round ;) |
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 for being patient with me @olcbean! I left one last thing. I just hadn't communicated clearly enough what I wanted.
} | ||
|
||
private static boolean getRequired(XContentParser parser) throws IOException { | ||
ObjectParser<Parameter, Void> objParser = new ObjectParser<>("", true, Parameter::new); |
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.
Ah! More like:
private static final ObjectParser<Parameter, Void> PARAMETER_PARSER = new ObjectParser<>("parameter", true, Parameter:true);
static {
PARAMETER_PARSER.declareBoolean(Parameter::setRequired, new ParseField("required");
}
And they you can call it like PARAMETER_PARSER.parse(parser, null).isRequired()
like you have. That way we reuse the parser. It probably isn't a big deal, but it is what we do everywhere else so we may as well do it here.
} | ||
} | ||
|
||
if (!apiRequiredPathParts.isEmpty()) { |
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.
Funny story: we had a contributor for a long time that always wanted false == blah
rather than !blah
because, he claimed, it is harder to miss the false ==
part then the !
part. I kind of agree with him but I think we were in the minority. But that is why you have both patterns all over our code base. Either way is fine.
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.
Hope you don't mind that I change all occurrences of !
to false ==
(consistency on file level)
@nik9000 thanks for your patience! |
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.
Looks good to me. I'm going to run it through the tests locally and merge it when it passes.
cool stuff, thanks @olcbean LGTM as well. |
…parameters (#27035) Till now the yaml test runner was verifying that the provided path parts and parameters are supported. With this PR, yaml test runner also checks that all required path parts and parameters are provided.
I've (finally) merge this to master and 6.x. |
Till now the yaml test runner was verifying that the provided path parts and parameters are supported.
With this PR, yaml test runner also checks that all required path parts and parameters are provided.
Related to #25737