diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ClientYamlTestClient.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ClientYamlTestClient.java index 152af0c85f631..b4704bd9ed896 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ClientYamlTestClient.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/ClientYamlTestClient.java @@ -19,6 +19,7 @@ package org.elasticsearch.test.rest.yaml; import com.carrotsearch.randomizedtesting.RandomizedTest; + import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; @@ -42,6 +43,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; /** * Used by {@link ESClientYamlSuiteTestCase} to execute REST requests according to the tests written in yaml suite files. Wraps a @@ -80,24 +84,40 @@ public ClientYamlTestResponse callApi(String apiName, Map params //divide params between ones that go within query string and ones that go within path Map pathParts = new HashMap<>(); Map queryStringParams = new HashMap<>(); + + Set apiRequiredPathParts = restApi.getPathParts().entrySet().stream().filter(e -> e.getValue() == true).map(Entry::getKey) + .collect(Collectors.toSet()); + Set apiRequiredParameters = restApi.getParams().entrySet().stream().filter(e -> e.getValue() == true).map(Entry::getKey) + .collect(Collectors.toSet()); + for (Map.Entry entry : params.entrySet()) { - if (restApi.getPathParts().contains(entry.getKey())) { + if (restApi.getPathParts().containsKey(entry.getKey())) { pathParts.put(entry.getKey(), entry.getValue()); + apiRequiredPathParts.remove(entry.getKey()); + } else if (restApi.getParams().containsKey(entry.getKey()) + || restSpec.isGlobalParameter(entry.getKey()) + || restSpec.isClientParameter(entry.getKey())) { + queryStringParams.put(entry.getKey(), entry.getValue()); + apiRequiredParameters.remove(entry.getKey()); } else { - if (restApi.getParams().contains(entry.getKey()) || restSpec.isGlobalParameter(entry.getKey()) - || restSpec.isClientParameter(entry.getKey())) { - queryStringParams.put(entry.getKey(), entry.getValue()); - } else { - throw new IllegalArgumentException("param [" + entry.getKey() + "] not supported in [" - + restApi.getName() + "] " + "api"); - } + throw new IllegalArgumentException( + "path/param [" + entry.getKey() + "] not supported by [" + restApi.getName() + "] " + "api"); } } + if (false == apiRequiredPathParts.isEmpty()) { + throw new IllegalArgumentException( + "missing required path part: " + apiRequiredPathParts + " by [" + restApi.getName() + "] api"); + } + if (false == apiRequiredParameters.isEmpty()) { + throw new IllegalArgumentException( + "missing required parameter: " + apiRequiredParameters + " by [" + restApi.getName() + "] api"); + } + List supportedMethods = restApi.getSupportedMethods(pathParts.keySet()); String requestMethod; if (entity != null) { - if (!restApi.isBodySupported()) { + if (false == restApi.isBodySupported()) { throw new IllegalArgumentException("body is not supported by [" + restApi.getName() + "] api"); } String contentType = entity.getContentType().getValue(); diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApi.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApi.java index 18784f5ce9ae4..72c94762a4314 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApi.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApi.java @@ -22,6 +22,7 @@ import org.apache.http.client.methods.HttpPut; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -35,8 +36,8 @@ public class ClientYamlSuiteRestApi { private final String name; private List methods = new ArrayList<>(); private List paths = new ArrayList<>(); - private List pathParts = new ArrayList<>(); - private List params = new ArrayList<>(); + private Map pathParts = new HashMap<>(); + private Map params = new HashMap<>(); private Body body = Body.NOT_SUPPORTED; public enum Body { @@ -98,20 +99,28 @@ void addPath(String path) { this.paths.add(path); } - public List getPathParts() { + /** + * Gets all path parts supported by the api. For every path part defines if it + * is required or optional. + */ + public Map getPathParts() { return pathParts; } - void addPathPart(String pathPart) { - this.pathParts.add(pathPart); + void addPathPart(String pathPart, boolean required) { + this.pathParts.put(pathPart, required); } - public List getParams() { + /** + * Gets all parameters supported by the api. For every parameter defines if it + * is required or optional. + */ + public Map getParams() { return params; } - void addParam(String param) { - this.params.add(param); + void addParam(String param, boolean required) { + this.params.put(param, required); } void setBodyOptional() { diff --git a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApiParser.java b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApiParser.java index 8abcfc35f27a3..66dc0a5705400 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApiParser.java +++ b/test/framework/src/main/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApiParser.java @@ -18,6 +18,8 @@ */ package org.elasticsearch.test.rest.yaml.restspec; +import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentParser; import java.io.IOException; @@ -27,6 +29,11 @@ */ public class ClientYamlSuiteRestApiParser { + private static final ObjectParser PARAMETER_PARSER = new ObjectParser<>("parameter", true, Parameter::new); + static { + PARAMETER_PARSER.declareBoolean(Parameter::setRequired, new ParseField("required")); + } + public ClientYamlSuiteRestApi parse(String location, XContentParser parser) throws IOException { while ( parser.nextToken() != XContentParser.Token.FIELD_NAME ) { @@ -57,7 +64,6 @@ public ClientYamlSuiteRestApi parse(String location, XContentParser parser) thro if (parser.currentToken() == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); } - if (parser.currentToken() == XContentParser.Token.START_ARRAY && "paths".equals(currentFieldName)) { while (parser.nextToken() == XContentParser.Token.VALUE_STRING) { String path = parser.text(); @@ -71,30 +77,30 @@ public ClientYamlSuiteRestApi parse(String location, XContentParser parser) thro if (parser.currentToken() == XContentParser.Token.START_OBJECT && "parts".equals(currentFieldName)) { while (parser.nextToken() == XContentParser.Token.FIELD_NAME) { String part = parser.currentName(); - if (restApi.getPathParts().contains(part)) { + if (restApi.getPathParts().containsKey(part)) { throw new IllegalArgumentException("Found duplicate part [" + part + "]"); } - restApi.addPathPart(part); parser.nextToken(); if (parser.currentToken() != XContentParser.Token.START_OBJECT) { throw new IllegalArgumentException("Expected parts field in rest api definition to contain an object"); } - parser.skipChildren(); + restApi.addPathPart(part, PARAMETER_PARSER.parse(parser, null).isRequired()); } } if (parser.currentToken() == XContentParser.Token.START_OBJECT && "params".equals(currentFieldName)) { while (parser.nextToken() == XContentParser.Token.FIELD_NAME) { + String param = parser.currentName(); - if (restApi.getParams().contains(param)) { + if (restApi.getParams().containsKey(param)) { throw new IllegalArgumentException("Found duplicate param [" + param + "]"); } - restApi.addParam(parser.currentName()); + parser.nextToken(); if (parser.currentToken() != XContentParser.Token.START_OBJECT) { throw new IllegalArgumentException("Expected params field in rest api definition to contain an object"); } - parser.skipChildren(); + restApi.addParam(param, PARAMETER_PARSER.parse(parser, null).isRequired()); } } @@ -124,7 +130,7 @@ public ClientYamlSuiteRestApi parse(String location, XContentParser parser) thro } } } - if (!requiredFound) { + if (false == requiredFound) { restApi.setBodyOptional(); } } @@ -146,4 +152,14 @@ public ClientYamlSuiteRestApi parse(String location, XContentParser parser) thro return restApi; } + + private static class Parameter { + private boolean required; + public boolean isRequired() { + return required; + } + public void setRequired(boolean required) { + this.required = required; + } + } } diff --git a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApiParserTests.java b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApiParserTests.java index 6acdd935400f4..ddf89f9a6fcca 100644 --- a/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApiParserTests.java +++ b/test/framework/src/test/java/org/elasticsearch/test/rest/yaml/restspec/ClientYamlSuiteRestApiParserTests.java @@ -22,7 +22,9 @@ import org.elasticsearch.test.rest.yaml.section.AbstractClientYamlTestFragmentParserTestCase; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasEntry; import static org.hamcrest.Matchers.notNullValue; public class ClientYamlSuiteRestApiParserTests extends AbstractClientYamlTestFragmentParserTestCase { @@ -39,11 +41,13 @@ public void testParseRestSpecIndexApi() throws Exception { assertThat(restApi.getPaths().get(0), equalTo("/{index}/{type}")); assertThat(restApi.getPaths().get(1), equalTo("/{index}/{type}/{id}")); assertThat(restApi.getPathParts().size(), equalTo(3)); - assertThat(restApi.getPathParts().get(0), equalTo("id")); - 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(), hasEntry("index", true)); + assertThat(restApi.getPathParts(), hasEntry("type", true)); + assertThat(restApi.getPathParts(), hasEntry("id", false)); assertThat(restApi.getParams().size(), equalTo(4)); - assertThat(restApi.getParams(), contains("wait_for_active_shards", "op_type", "parent", "refresh")); + assertThat(restApi.getParams().keySet(), containsInAnyOrder("wait_for_active_shards", "op_type", "parent", "refresh")); + restApi.getParams().entrySet().stream().forEach(e -> assertThat(e.getValue(), equalTo(false))); assertThat(restApi.isBodySupported(), equalTo(true)); assertThat(restApi.isBodyRequired(), equalTo(true)); } @@ -59,7 +63,7 @@ public void testParseRestSpecGetTemplateApi() throws Exception { assertThat(restApi.getPaths().get(0), equalTo("/_template")); assertThat(restApi.getPaths().get(1), equalTo("/_template/{name}")); assertThat(restApi.getPathParts().size(), equalTo(1)); - assertThat(restApi.getPathParts().get(0), equalTo("name")); + assertThat(restApi.getPathParts(), hasEntry("name", false)); assertThat(restApi.getParams().size(), equalTo(0)); assertThat(restApi.isBodySupported(), equalTo(false)); assertThat(restApi.isBodyRequired(), equalTo(false)); @@ -78,10 +82,11 @@ public void testParseRestSpecCountApi() throws Exception { assertThat(restApi.getPaths().get(1), equalTo("/{index}/_count")); assertThat(restApi.getPaths().get(2), equalTo("/{index}/{type}/_count")); assertThat(restApi.getPathParts().size(), equalTo(2)); - assertThat(restApi.getPathParts().get(0), equalTo("index")); - assertThat(restApi.getPathParts().get(1), equalTo("type")); + assertThat(restApi.getPathParts().keySet(), containsInAnyOrder("index", "type")); + restApi.getPathParts().entrySet().stream().forEach(e -> assertThat(e.getValue(), equalTo(false))); assertThat(restApi.getParams().size(), equalTo(1)); - assertThat(restApi.getParams().get(0), equalTo("ignore_unavailable")); + assertThat(restApi.getParams().keySet(), contains("ignore_unavailable")); + assertThat(restApi.getParams(), hasEntry("ignore_unavailable", false)); assertThat(restApi.isBodySupported(), equalTo(true)); assertThat(restApi.isBodyRequired(), equalTo(false)); }