avaje / avaje-http Goto Github PK
View Code? Open in Web Editor NEWController generation for Javalin, Helidon SE.
Home Page: https://avaje.io/http/
License: Apache License 2.0
Controller generation for Javalin, Helidon SE.
Home Page: https://avaje.io/http/
License: Apache License 2.0
So for example when marshalling into @Post
@Form
the following dto:
// our form bean ... with Kotlin non-nullable types
data class HelloForm(var email: String, var url: String, var description: String)
// used by @Form @Post controller method
@Form @Post("asform")
fun saveForm(hi: HelloForm) {
println("saving $hi")
}
The generated code understands that the properties are Kotlin non-nullable types so checks for null when populating HelloForm.
The generated code is:
ApiBuilder.post("/hello/asform", ctx -> {
ctx.status(201);
HelloForm hi = new HelloForm(
checkNull(ctx.formParam("email"), "email"),
checkNull(ctx.formParam("url"), "url"),
checkNull(ctx.formParam("description"), "description")
);
controller.saveForm(hi);
});
Then if we post to that form and don't have a property we get an exception like:
io.dinject.controller.RequiredArgumentException: Required property description was not supplied.
at io.dinject.controller.PathTypeConversion.checkNull(PathTypeConversion.java:26)
at org.example.web.HelloController$route.lambda$registerRoutes$2(HelloController$route.java:43)
...
So we typically register an exception handler for io.dinject.controller.RequiredArgumentException
app.exception(RequiredArgumentException::class.java) { exception, ctx ->
val map = LinkedHashMap<String, String?>()
map["message"] = exception.message
ctx.json(map)
ctx.status(400)
}
This is nice in that it reads javadoc (for Java and Kotlin) for controller methods and parameters so we can generate nice OpenAPI documentation without hardly any extra annotations.
Effectively that means the controllers with these "request scoped dependencies" become request scoped rather than singleton scoped. dinject will detect this and generate BeanFactory class to match and these will be used by the generated web route.
@Controller
@Path("/req-scoped")
class ReqScopedController {
@Inject
Context context; // inject the Javalin context (so will be treated as request scoped)
@Produces("text/plain")
@Get
String getSimple() {
return context.url();
}
}
For Helidon
@Controller
@Path("/req-scoped")
public class ReqScopedController {
@Inject
ServerRequest request; // inject helidon request
@Inject
ServerResponse response; // inject helidon response
...
}
Hi Rob,
I was using the newest libraries (avaje-http-javalin-generator:1.0, avaje-inject-generator:2.0 ) for a small new service that I wanted to build in Kotlin but it seams not to work.
My code is:
@controller
@path("/zipcode")
class ZipCodeController {
@get("/zipcode/{zipcode}")
@throws(NotFoundException::class)
fun getAddress(zipcode: String): ZipCodeResponse {
val zipCodeResult = QZipCode().zipCode.eq(zipcode).findOne() ?: throw NotFoundException("Zipcode not found")
return ZipCodeResponse(
zipCodeResult.zipCode,
zipCodeResult.streetName,
zipCodeResult.placeOfResidence
)
}
}
Which then generates into a route file:
@OverRide
public void registerRoutes() {
ApiBuilder.get("/zipcode/zipcode/:zipcode", ctx -> {
ctx.status(200);
String zipcode = ctx.pathParam("zipcode");
ctx.json(controller.getAddress(zipcode));
});
}
Then i go to the webpage and i get the "invalid path argument".
If I build the same in Java it works.
Also when I use a Get without parameters it also works in kotlin.
Now I am not sure if this is a issue with this libary or with Javalin itself.
Hopefully you know where the issue lies or can point where to look futher,
Tijs
Generated controller class sets ctx.status after calling the registered route handler and overwrites previously set status code. An easy fix would be moving the status() call before the handler turning the behaviour to a fallback.
ApiBuilder.post("/login", ctx -> {
controller.login(ctx);
ctx.status(201);
});
Hi @rbygrave
I have a question regard to passing the permitted roles into controller methods @Get
, @Post
, @Put
and @Delete
: do we support for the usage of AccessManager
from javalin?
from Javalin
, from setting route, we can pass a set of permitted Role
then the configured AccessManager will be kicked in and work per endpoint setup. However, I don't see any generated code to handle Role.
Regards,
Travis
Hi Rob,
I am looking at the generated openapi.json file and I am not sure it is correct.
I think this because a client that I generate from the openapi.json file cannot use the Path Parameters.
If I am correct I need to specify a path parameter like this:
@Get("/animal/:id") public Animal getOne(Long id)
But then it generates a openapi.json file like this:
"paths" : { "/animal/one/:id" : { "get" : { "parameters" : [ { "name" : "id", "in" : "path", "required" : true, "schema" : { "type" : "integer", "format" : "int64" } } ], },
It looks correct but I think that the generated path should look like this: "/animal/one/{id}"
when I follow the documentation:
https://swagger.io/docs/specification/describing-parameters/
Also when I look at the client I generated with the dart generator it needs the {} around the parameters.
An example from the generated client is like this:
String path = "/animal/one/:id".replaceAll("{format}","json").replaceAll("{" + "id" + "}", id.toString());
Kind regards,
Tijs
With this we can use Map<String, ?>
parameter types for @QueryParam
, @Header
and @FormParam
and these are then passed as a map to the set multiple query parameters, headers, or form parameters.
@Get("{uid}/withParams")
HttpResponse<String> withParams(long uid, @QueryParam Map<String, ?> params);
Generates the code below ...
// GET {uid}/withParams
@Override
public HttpResponse<String> withParams(long uid, Map<String,?> params) {
return clientContext.request()
.path("moo").path(uid).path("withParams")
.queryParam(params)
.GET()
.asString();
}
Note that the .queryParam(params)
sets the map of query parameters to the request.
Generated the ctx.json(
prior to the validator.validate()
call.
The fix adjusts the order such that ctx.json(
is generated after.
Hi Rob,
Something has changed with the new version.
The following code compiles with version 1.18 of the generator but not with the newest master branch.
`
import io.dinject.controller.Controller;
import io.dinject.controller.Path;
@controller
@path("/hallo")
public class api {
public String test() {
return "TEST";
}
}
`
It took me some time find it but it appears that it goes wrong in the constructor of the MethodReader
and than specifically this line:
this.pathSegments = PathSegments.parse(Util.combinePath(bean.getPath(), webMethodPath));
Even more specifically in the io.dinject.webroutegen.Util --> combinePath
because the webMethodPath is null
For a quick fix I just changed the lines inside the MethodReader from
this.pathSegments = PathSegments.parse(Util.combinePath(bean.getPath(), webMethodPath));
into
if(isWebMethod())
this.pathSegments = PathSegments.parse(Util.combinePath(bean.getPath(), webMethodPath));
else
this.pathSegments = PathSegments.EMPTY;
But no idea if I break something else, and if this is suppose to work like this
As per avaje-http-client 1.9
@Post
void asVoid();
@Post
HttpResponse<Void> asVoid2();
@Post
String asPlainString();
@Post
HttpResponse<String> asString2();
// @Post byte[] asBytesErr();
@Post
HttpResponse<byte[]> asBytes2();
// @Post InputStream asInputStreamErr();
@Post
HttpResponse<InputStream> asInputStream2();
// @Post Stream<String> asLinesErr();
@Post
HttpResponse<Stream<String>> asLines2();
@Post
Repo bean();
@Post
List<Repo> list();
@Post
Stream<Repo> stream();
// -------
// @Post CompletableFuture<Void> cfVoidErr();
@Post
CompletableFuture<HttpResponse<Void>> cfVoid();
// @Post CompletableFuture<String> cfStringErr();
@Post
CompletableFuture<HttpResponse<String>> cfString();
// @Post CompletableFuture<byte[]> cfBytesErr();
@Post
CompletableFuture<HttpResponse<byte[]>> cfBytes();
// @Post CompletableFuture<InputStream> cfInputStreamErr2();
@Post
CompletableFuture<HttpResponse<InputStream>> cfInputStream();
// @Post CompletableFuture<Stream<String>> cfLinesErr();
@Post
CompletableFuture<HttpResponse<Stream<String>>> cfLines();
// @Post CompletableFuture<Void> cfVoidErr();
@Post
HttpCall<HttpResponse<Void>> callVoid();
// @Post CompletableFuture<String> cfStringErr();
@Post
HttpCall<HttpResponse<String>> callString();
// @Post HttpCall<byte[]> callBytesErr();
@Post
HttpCall<HttpResponse<byte[]>> callBytes();
// @Post HttpCall<InputStream> callInputStreamErr();
@Post
HttpCall<HttpResponse<InputStream>> callInputStream();
// @Post HttpCall<Stream<String>> callLinesErr();
@Post
HttpCall<HttpResponse<Stream<String>>> callLines();
@Client
@Path("users")
public interface GitHubUsers {
@Get("{user}/repos")
List<Repo> listRepos(String user);
}
The client annotation processor then generates an implementation of GitHubUsers
To generate a client when the API is defined in another module use @Client.Import
@Client.Import(types = OtherApi.class)
package org.example;
Use avaje-inject version 3.1 to stay with generated code using java.inject
To turn on bean validation we currently do that per controller. It would be nice to be able to do that per method rather than per controller.
I tried to run the project with gradle 5.3.1 (and some other 5.x) versions, but gradle is not working.
Javalin is working without problems, but there are no Beans registered.
The SystemContext.getBean can't use defined classes and the automatic Bean registrator is not working.
I think that the problem is the pre-compiler, they don't compile an register the beans.
My fix is, to download the gradle wrapper version 4.10.
Add nullable=false, max & min lengths and format email.
Hi Rob,
I was looking into a way to add endpoint from another lib to the openapi documentation of the final service.
For example I have a base project y that provides a general /health or /info endpoint and I use that lib in project z.
The endpoints are made available with the use of your inject project when I start the application, but they are not present in the openapi documentation.
Now I was wondering and trying out but could not achieve this by looking for the "Controller" classes and trying to add them but it is not looking to other inherited projects.
Would something like this be possible in combination with the @ContextModule[depenson=project y] so we know where to look and then add those endpoints? Because it would be nice to add all available endpoints to the final documentation.
Kind regards,
Tijs
So for Javalin we can do:
@Get("{id}/{type}") // <!-- more standard style using {}
List<Foo> getFoos(long id, String type) ...
@Get(":id/:type") // <!-- Javalin colon style
List<Foo> getFoos(long id, String type) ...
Hi @rbygrave,
I wonder can we have validation annotation for bean param? I have a look at generated code for javalin and don't see any part for validation despite of having annotation on bean param class.
Thanks,
Travis
For example: Given the form bean:
@Valid
public class HelloForm {
@NotNull
String name;
@Email @Size(max = 100)
String email;
@URL
String url;
@Future
LocalDate startDate;
public HelloForm(String name, String email) {
this.name = name;
this.email = email;
}
...
With the controller method of:
@Form @Post("saveform")
void saveForm(HelloForm helloForm) {
...
}
Then the generated code will populate the form bean using the constructor:
The generated code is:
ApiBuilder.post("/hello/saveform", ctx -> {
ctx.status(201);
HelloForm helloForm = new HelloForm(
ctx.formParam("name"),
ctx.formParam("email")
);
helloForm.url = ctx.formParam("url");
helloForm.startDate = toLocalDate(ctx.formParam("startDate"));
validator.validate(helloForm);
controller.saveForm(helloForm);
});
Hi Rob,
Not to reopen the other ticket I use a new one.
But my idea of using the ${revision} seems to work ok, but not with the flatten plugin. There a few bugs which makes it very difficult for me to use it. There are already tickets for it but I think the library is to big and does to much for what I want and that is rewrite the ${revision} to a version.
Now I found https://github.com/jcgay/unique-revision-maven-filtering which does just that, but it also had two issues, not updating the ${revision} on dependencies and removing the relativePath before posting.
I created a pull request there but if you are interested this is the branch I updated and am using:
https://github.com/Tijs-2/unique-revision-maven-filtering/tree/revision_for_dependencies
Not sure if you are still interested in this but I thought I send an update.
Tijs
Detect the version and generate appropriately
Doing this because we can and this is likely that people do this a lot.
That is:
Treat FooForm
method parameter as if was annotated with @Form
(because it can't be anything else).
@Post("other") @Form
void postFormOther(FooForm fooForm, Context ctx) {
...
}
The syntax implied by similarity to JAX-RS @BeanParam
... is to annotate the method parameter explicitly like below (which is what we support).
... but actually FooForm can't be anything but a "form bean" (it is not Java Context and not one of our known scalar types - String, Long, LocalDate ... etc).
@Post("other")
void postFormOther(@Form FooForm fooForm, Context ctx) {
...
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.