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); } }