Giter VIP home page Giter VIP logo

avaje-http's People

Contributors

dependabot[bot] avatar github-actions[bot] avatar kevin70 avatar loonyrules avatar maxtuzz avatar mechite avatar mrguamos avatar nschlehe avatar ponyjoy avatar rbygrave avatar rob-bygrave avatar sebastian-mrozek avatar sentryman avatar tijs-2 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

avaje-http's Issues

ENH: Support Kotlin non-nullable properties and constructor params

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

Errors

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

Support @Inject of Javalin Context and Helidon ServerRequest/ServerResponse

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 
  ...
}

Kotlin: "invalid path argument"

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

HTTP status code can't be overwritten by route handler

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

Passing Set<Role> parameter for controller method

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

Path parameters

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

ENH: [Client generator] Add Map<String,?> option for header() queryParam() and formParam()

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.

Example

  @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.

Methods with no GET/POST etc inside Controller class

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

Support client code generation for CompletableFuture and HttpCall with various generic types

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

Support client generation with @Client and @Client.Import

@Client
@Path("users")
public interface GitHubUsers {

  @Get("{user}/repos")
  List<Repo> listRepos(String user);

}

The client annotation processor then generates an implementation of GitHubUsers

Client.Import

To generate a client when the API is defined in another module use @Client.Import

@Client.Import(types = OtherApi.class)
package org.example;

DInject is not working with Gradle 5.3.1

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.

Adding endpoint from other libs to openapi documentation

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

ENH: Support non-default constructor for form beans

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

Multi module build

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

Have @Form on method imply a bean is a "form bean" (not require @Form on method param)

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) {
    ...
  }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.