package org.jooby.raml;
import static org.junit.Assert.assertEquals;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import org.jooby.MediaType;
import org.jooby.Upload;
import org.jooby.internal.raml.RamlBuilder;
import org.jooby.spec.RouteParam;
import org.jooby.spec.RouteParamType;
import org.jooby.spec.RouteResponse;
import org.jooby.spec.RouteSpec;
import org.junit.Test;
import com.google.inject.util.Types;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigValueFactory;
public class RamlBuilderTest {
public static class Param implements RouteParam {
private String name;
private Type type;
private RouteParamType paramType;
private Object value;
public Param(final String name, final Type type, final RouteParamType paramType,
final Object value, final String doc) {
this.name = name;
this.type = type;
this.paramType = paramType;
this.value = value;
this.doc = doc;
}
public Param(final String name, final Type type, final RouteParamType paramType) {
this(name, type, paramType, null, null);
}
public Param(final String name, final Type type, final RouteParamType paramType,
final String doc) {
this(name, type, paramType, null, doc);
}
private String doc;
@Override
public String name() {
return name;
}
@Override
public Type type() {
return type;
}
@Override
public RouteParam type(final Type type) {
this.type = type;
return this;
}
@Override
public RouteParamType paramType() {
return paramType;
}
@Override
public RouteParam paramType(final RouteParamType type) {
paramType = type;
return this;
}
@Override
public Object value() {
return value;
}
@Override
public Optional<String> doc() {
return Optional.ofNullable(doc);
}
}
@SuppressWarnings("serial")
public static class Response implements RouteResponse {
private Type type = Object.class;
private String doc;
private Map<Integer, String> statusCodes = new LinkedHashMap<>();
public Response() {
}
@Override
public Type type() {
return type;
}
public Response type(final Type type) {
this.type = type;
return this;
}
@Override
public Optional<String> doc() {
return Optional.ofNullable(doc);
}
public Response doc(final String doc) {
this.doc = doc;
return this;
}
@Override
public Map<Integer, String> statusCodes() {
return statusCodes;
}
public Response status(final int status, final String code) {
statusCodes.put(status, code);
return this;
}
}
@SuppressWarnings("serial")
public static class Spec implements RouteSpec {
String summary;
String name;
String method;
String pattern;
String doc;
List<String> consumes = Arrays.asList("*/*");
List<String> produces = Arrays.asList("*/*");
List<RouteParam> params = new ArrayList<>();
Response rsp = new Response();
@Override
public Optional<String> summary() {
return Optional.ofNullable(summary);
}
public Spec summary(final String summary) {
this.summary = summary;
return this;
}
@Override
public Optional<String> name() {
return Optional.ofNullable(name);
}
public Spec setName(final String name) {
this.name = name;
return this;
}
@Override
public String method() {
return method;
}
public Spec method(final String method) {
this.method = method;
return this;
}
@Override
public String pattern() {
return pattern;
}
public Spec pattern(final String pattern) {
this.pattern = pattern;
return this;
}
@Override
public Optional<String> doc() {
return Optional.ofNullable(doc);
}
public Spec doc(final String doc) {
this.doc = doc;
return this;
}
@Override
public List<String> consumes() {
return consumes;
}
public Spec consumes(final String... consumes) {
this.consumes = Arrays.asList(consumes);
return this;
}
@Override
public List<String> produces() {
return produces;
}
public Spec produces(final String... produces) {
this.produces = Arrays.asList(produces);
return this;
}
@Override
public List<RouteParam> params() {
return params;
}
public Spec params(final RouteParam... params) {
this.params = Arrays.asList(params);
return this;
}
@Override
public RouteResponse response() {
return rsp;
}
public Spec rsp(final Consumer<Response> rsp) {
rsp.accept(this.rsp);
return this;
}
@Override
public Map<String, Object> attributes() {
return Collections.emptyMap();
}
}
private Config conf = ConfigFactory.empty()
.withValue("mediaType", ConfigValueFactory.fromAnyRef("application/json"));
@Test
public void routes() {
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("GET", "/api/pets"),
route("GET", "/api/pets/:id"),
route("POST", "/api/pets"),
route("DELETE", "/api/pets/:id")));
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"/api:\n" +
" /pets:\n" +
" get:\n" +
" post:\n" +
" /{id}:\n" +
" get:\n" +
" delete:", raml);
}
@Test
public void moreRoutes() {
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("GET", "/api/pets"),
route("GET", "/api/pets/:id"),
route("POST", "/api/pets"),
route("DELETE", "/api/pets/:id"),
route("GET", "/api/people"),
route("GET", "/api/people/:id")));
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"/api:\n" +
" /pets:\n" +
" get:\n" +
" post:\n" +
" /{id}:\n" +
" get:\n" +
" delete:\n" +
" /people:\n" +
" get:\n" +
" /{id}:\n" +
" get:", raml);
}
@Test
public void moreRoutes2() {
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("GET", "/api/pets"),
route("GET", "/api/pets/:id"),
route("POST", "/api/pets"),
route("DELETE", "/api/pets/:id"),
route("GET", "/api/people/:id")));
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"/api:\n" +
" /pets:\n" +
" get:\n" +
" post:\n" +
" /{id}:\n" +
" get:\n" +
" delete:\n" +
" /people/{id}:\n" +
" get:", raml);
}
@Test
public void routes2() {
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("GET", "/api/pets")));
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"/api/pets:\n" +
" get:", raml);
}
@Test
public void uriParams() {
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("GET", "/users/:userId", path("userId", int.class, "The id of the user"))))
.trim();
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"/users/{userId}:\n" +
" uriParameters:\n" +
" userId:\n" +
" type: integer\n" +
" description: 'The id of the user'\n" +
" required: true\n" +
" get:", raml);
}
@Test
public void queryParams() {
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("GET", "/users/:userId", path("userId", int.class, "The id of the user"),
query("internal", int.class))))
.trim();
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"/users/{userId}:\n" +
" uriParameters:\n" +
" userId:\n" +
" type: integer\n" +
" description: 'The id of the user'\n" +
" required: true\n" +
" get:\n" +
" queryParameters:\n" +
" internal:\n" +
" type: integer\n" +
" required: true", raml);
}
@Test
public void formParam() {
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("POST", "/users", body("userId", int.class)).consumes(MediaType.form.name())))
.trim();
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"/users:\n" +
" post:\n" +
" body:\n" +
" application/x-www-form-urlencoded:\n" +
" formParameters:\n" +
" type: integer", raml);
}
@Test
public void bodyParam() {
String doc = "<p>Enters the file content for an existing song entity.</p>" +
" <ul>\n"
+ " <li>Use the <code>binary/octet-stream</code> content type to specify the content from any consumer (excepting web-browsers).</li>\n"
+
" <li>Use the <code>multipart-form/data</code> content type to upload a file which content will become the file-content</li>\n"
+ "</ul>\n";
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("POST", "/users", doc, body("userId", int.class)).consumes("application/json")
.produces("application/json")))
.trim();
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"/users:\n" +
" post:\n" +
" description: |-\n" +
" Enters the file content for an existing song entity. \n\n" +
" * Use the `binary/octet-stream` content type to specify the content from any consumer (excepting web-browsers).\n\n"
+
" * Use the `multipart-form/data` content type to upload a file which content will become the file-content\n"
+
" body:\n" +
" application/json:\n" +
" type: integer", raml);
}
@Test
public void fileParam() {
String doc = "<p>Enters the file content for an existing song entity.</p>" +
" <ul>\n"
+ " <li>Use the <code>binary/octet-stream</code> content type to specify the content from any consumer (excepting web-browsers).</li>\n"
+
" <li>Use the <code>multipart-form/data</code> content type to upload a file which content will become the file-content</li>\n"
+ "</ul>\n";
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("POST", "/users", doc, file("file"))))
.trim();
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"/users:\n" +
" post:\n" +
" description: |-\n" +
" Enters the file content for an existing song entity. \n\n" +
" * Use the `binary/octet-stream` content type to specify the content from any consumer (excepting web-browsers).\n\n"
+
" * Use the `multipart-form/data` content type to upload a file which content will become the file-content\n"
+
" body:\n" +
" multipart/form-data:\n" +
" formParameters:\n" +
" file:\n" +
" type: file\n" +
" required: true", raml);
}
@Test
public void rsp() {
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("GET", "/users/:userId", path("userId", int.class, "The id of the user"))
.rsp(rsp -> rsp.type(Person.class).status(200, "Success"))))
.trim();
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"types:\n" +
" Person:\n" +
" type: object\n" +
" properties:\n" +
" name:\n" +
" type: string\n" +
" parent:\n" +
" type: Person\n" +
" children:\n" +
" type: Person[]\n" +
" age:\n" +
" type: integer\n" +
" required: false\n" +
"/users/{userId}:\n" +
" uriParameters:\n" +
" userId:\n" +
" type: integer\n" +
" description: 'The id of the user'\n" +
" required: true\n" +
" get:\n" +
" responses:\n" +
" 200:\n" +
" body:\n" +
" application/json:\n" +
" type: Person", raml);
}
@Test
public void rsp2() {
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("GET", "/users/:userId", path("userId", int.class, "The id of the user"))
.rsp(
rsp -> rsp.type(Person.class).status(200, "Success").status(404,
"Not found\nNextLine"))))
.trim();
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"types:\n" +
" Person:\n" +
" type: object\n" +
" properties:\n" +
" name:\n" +
" type: string\n" +
" parent:\n" +
" type: Person\n" +
" children:\n" +
" type: Person[]\n" +
" age:\n" +
" type: integer\n" +
" required: false\n" +
"/users/{userId}:\n" +
" uriParameters:\n" +
" userId:\n" +
" type: integer\n" +
" description: 'The id of the user'\n" +
" required: true\n" +
" get:\n" +
" responses:\n" +
" 200:\n" +
" body:\n" +
" application/json:\n" +
" type: Person\n" +
" 404:\n" +
" description: |-\n" +
" Not found\n" +
" NextLine", raml);
}
@Test
public void issue534() {
String raml = new RamlBuilder(conf)
.build(Arrays.asList(route("GET", "/")));
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"/:\n" +
" get:", raml);
}
@Test
public void rsp3() {
String raml = new RamlBuilder(conf)
.build(Arrays.asList(
route("GET", "/users/:userId", path("userId", int.class, "The id of the user"))
.rsp(rsp -> rsp.type(Types.listOf(Person.class))
.doc("Some doc")
.status(200, "Success").status(404, "Not found"))))
.trim();
assertEquals("#%RAML 1.0\n" +
"mediaType: application/json\n" +
"types:\n" +
" Person:\n" +
" type: object\n" +
" properties:\n" +
" name:\n" +
" type: string\n" +
" parent:\n" +
" type: Person\n" +
" children:\n" +
" type: Person[]\n" +
" age:\n" +
" type: integer\n" +
" required: false\n" +
"/users/{userId}:\n" +
" uriParameters:\n" +
" userId:\n" +
" type: integer\n" +
" description: 'The id of the user'\n" +
" required: true\n" +
" get:\n" +
" responses:\n" +
" 200:\n" +
" description: 'Some doc'\n" +
" body:\n" +
" application/json:\n" +
" type: Person[]\n" +
" 404:\n" +
" description: 'Not found'", raml);
}
Param path(final String name, final Type type) {
return param(name, RouteParamType.PATH, type);
}
Param body(final String name, final Type type) {
return param(name, RouteParamType.BODY, type);
}
Param file(final String name) {
return param(name, RouteParamType.FILE, Upload.class);
}
Param query(final String name, final Type type) {
return param(name, RouteParamType.QUERY, type);
}
Param path(final String name, final Type type, final String doc) {
return param(name, RouteParamType.PATH, type, doc);
}
Param param(final String name, final RouteParamType paramType, final Type type) {
return param(name, paramType, type, null);
}
Param param(final String name, final RouteParamType paramType, final Type type,
final String doc) {
return new Param(name, type, paramType, doc);
}
Spec route(final String method, final String pattern, final RouteParam... param) {
return new Spec().method(method).pattern(pattern).params(param);
}
Spec route(final String method, final String pattern, final String doc,
final RouteParam... param) {
return new Spec().method(method).pattern(pattern).doc(doc).params(param);
}
}