Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add BigInteger param type mapping #285

Merged
merged 8 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 31 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ public class WidgetController {
}
```
## DI Usage
The annotation processor will generate controller adapters to register routes to Javalin/Helidon. The natural way to use the generated adapters is to get a DI library to find and wire them. This is what the examples below do; they use [Avaje-Inject](https://avaje.io/inject/) to do this. The AP will automatically detect the presence of avaje-inject and generate the class to use avaje-inject's `@Component` as the DI annotation.
The annotation processor will generate controller adapters to register routes to Javalin/Helidon. The natural way to use the generated adapters is to get a DI library to find and wire them. The AP will automatically detect the presence of avaje-inject and generate the class to use avaje-inject's `@Component` as the DI annotation.

There isn't a hard requirement to use Avaje for dependency injection. In the absence of avaje-inject, the generated class will use `@jakarta.inject.Singleton` or `@javax.inject.Singleton` depending on what's on the classpath. Any DI library that can find and wire the generated @Singleton beans can be used. You can even use Dagger2 or Guice to wire the controllers if you so desire.
There isn't a hard requirement to use [Avaje](https://avaje.io/inject/) for dependency injection. In the absence of avaje-inject, the generated class will use `@jakarta.inject.Singleton` or `@javax.inject.Singleton` depending on what's on the classpath. Any DI library that can find and wire the generated @Singleton beans can be used. You can even use Dagger2 or Guice to wire the controllers if you so desire.

To force the AP to generate with `@javax.inject.Singleton`(in the case where you have both jakarta and javax on the classpath), use the compiler arg `-AuseJavax=true`
```xml
Expand All @@ -93,22 +93,20 @@ To force the AP to generate with `@javax.inject.Singleton`(in the case where you

### Usage with Javalin

The annotation processor will generate controller classes implementing the Javalin `Plugin` interface, which means we can
get all the WebRoutes and register them with Javalin using:
The annotation processor will generate controller classes implementing the Javalin `Plugin` interface, which means we can register them with Javalin using:

```java
List<Plugin> routes = BeanScope.builder().build().list(Plugin.class);
List<Plugin> routes = ...; //retrieve using a DI framework

Javalin.create(cfg -> routes.forEach(cfg.plugins::register)).start();
```

### Usage with Helidon Nima
### Usage with Helidon SE (4.x)

The annotation processor will generate controller classes implementing the Helidon HttpService interface, which we can use
get all the services and register them with the Helidon `HttpRouting`.
The annotation processor will generate controller classes implementing the Helidon HttpFeature interface, which we can register with the Helidon `HttpRouting`.

```java
List<HttpFeature> routes = BeanScope.builder().build().list(HttpFeature.class);
List<HttpFeature> routes = ... //retrieve using a DI framework
final var builder = HttpRouting.builder();

routes.forEach(builder::addFeature);
Expand Down Expand Up @@ -153,14 +151,15 @@ public class WidgetController$Route implements Plugin {
}
```

### (Helidon Nima) The generated WidgetController$Route.java is:
### (Helidon SE) The generated WidgetController$Route.java is:

```java
@Generated("avaje-helidon-nima-generator")
@Singleton
@Generated("avaje-helidon-generator")
@Component
public class WidgetController$Route implements HttpFeature {

private final WidgetController controller;

public WidgetController$Route(WidgetController controller) {
this.controller = controller;
}
Expand All @@ -172,17 +171,19 @@ public class WidgetController$Route implements HttpFeature {
}

private void _getById(ServerRequest req, ServerResponse res) throws Exception {
res.status(OK_200);
var pathParams = req.path().pathParameters();
int id = asInt(pathParams.first("id").get());
var id = asInt(pathParams.first("id").get());
var result = controller.getById(id);
res.send(result);
}

private void _getAll(ServerRequest req, ServerResponse res) {
var pathParams = req.path().pathParameters();
private void _getAll(ServerRequest req, ServerResponse res) throws Exception {
res.status(OK_200);
var result = controller.getAll();
res.send(result);
}

}
```

Expand Down Expand Up @@ -225,21 +226,21 @@ public class WidgetController$Route implements Plugin {
}
```

### (Helidon Nima) The generated WidgetController$Route.java is:
### (Helidon SE) The generated WidgetController$Route.java is:

```java
@Generated("avaje-helidon-nima-generator")
@Generated("avaje-helidon-generator")
@Component
public class WidgetController$Route implements HttpFeature {

private final WidgetController controller;
private final JsonType<Widget> widgetJsonType;
private final JsonType<List<Widget>> listWidgetJsonType;
private final JsonType<WidgetController.Widget> widgetController$WidgetJsonType;
private final JsonType<List<WidgetController.Widget>> listWidgetController$WidgetJsonType;

public WidgetController$Route(WidgetController controller, Jsonb jsonB) {
public WidgetController$Route(WidgetController controller, Jsonb jsonb) {
this.controller = controller;
this.widgetJsonType = jsonB.type(Widget.class);
this.listWidgetJsonType = jsonB.type(Widget.class).list();
this.widgetController$WidgetJsonType = jsonb.type(WidgetController.Widget.class);
this.listWidgetController$WidgetJsonType = jsonb.type(WidgetController.Widget.class).list();
}

@Override
Expand All @@ -249,18 +250,20 @@ public class WidgetController$Route implements HttpFeature {
}

private void _getById(ServerRequest req, ServerResponse res) throws Exception {
res.status(OK_200);
var pathParams = req.path().pathParameters();
int id = asInt(pathParams.first("id").get());
var id = asInt(pathParams.first("id").get());
var result = controller.getById(id);
res.headers().contentType(HttpMediaType.APPLICATION_JSON);
widgetJsonType.toJson(result, JsonOutput.of(res));
res.headers().contentType(MediaTypes.APPLICATION_JSON);
widgetController$WidgetJsonType.toJson(result, JsonOutput.of(res));
}

private void _getAll(ServerRequest req, ServerResponse res) throws Exception {
var pathParams = req.path().pathParameters();
res.status(OK_200);
var result = controller.getAll();
res.headers().contentType(HttpMediaType.APPLICATION_JSON);
listWidgetJsonType.toJson(result, JsonOutput.of(res));
res.headers().contentType(MediaTypes.APPLICATION_JSON);
listWidgetController$WidgetJsonType.toJson(result, JsonOutput.of(res));
}

}
```
27 changes: 27 additions & 0 deletions http-api/src/main/java/io/avaje/http/api/PathTypeConversion.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.math.BigDecimal;
import java.time.*;
import java.math.BigInteger;
import java.util.List;
import java.util.Set;
import java.util.UUID;
Expand Down Expand Up @@ -131,6 +132,18 @@ public static boolean asBool(String value) {
return asBoolean(value);
}

/**
* Convert to BigInteger (not nullable).
*/
public static BigInteger asBigInteger(String value) {
SentryMan marked this conversation as resolved.
Show resolved Hide resolved
checkNull(value);
try {
return new BigInteger(value);
} catch (RuntimeException e) {
throw new InvalidPathArgumentException(e);
}
}

/**
* Convert to BigDecimal (not nullable).
*/
Expand Down Expand Up @@ -296,6 +309,20 @@ public static BigDecimal toBigDecimal(String value) {
}
}

/**
* Convert to BigInteger (allowing nulls).
*/
public static BigInteger toBigInteger(String value) {
if (isNullOrEmpty(value)) {
return null;
}
try {
return new BigInteger(value);
} catch (Exception e) {
throw new InvalidTypeArgumentException(e);
}
}

/**
* Convert to Boolean (allowing nulls).
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ private static void add(TypeHandler h) {

add(new UuidHandler());
add(new BigDecimalHandler());
add(new BigDecimalHandler());
add(new LocalDateHandler());
add(new LocalTimeHandler());
add(new LocalDateTimeHandler());
Expand Down Expand Up @@ -269,6 +270,12 @@ static class BigDecimalHandler extends ObjectHandler {
}
}

static class BigIntegerHandler extends ObjectHandler {
BigIntegerHandler() {
super("java.math.BigInteger", "BigInteger");
}
}

static class LocalDateHandler extends ObjectHandler {
LocalDateHandler() {
super("java.time.LocalDate", "LocalDate");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@ void writeHandler(boolean requestScoped) {
} else {
writer.append(" private void _%s(ServerRequest req, ServerResponse res) throws Exception {", method.simpleName()).eol();
}

if (!isFilter) {
int statusCode = method.statusCode();
if (statusCode > 0) {
writer.append(" res.status(%s);", lookupStatusCode(statusCode)).eol();
}
}

final var bodyType = method.bodyType();
if (bodyType != null && !method.isErrorMethod() && !isFilter) {
if ("InputStream".equals(bodyType)) {
Expand Down Expand Up @@ -253,10 +261,6 @@ private boolean usesFormParams() {
}

private void writeContextReturn() {
int statusCode = method.statusCode();
if (statusCode > 0) {
writer.append(" res.status(%s);", lookupStatusCode(statusCode)).eol();
}
final var producesOp = Optional.ofNullable(method.produces());
if (producesOp.isEmpty() && !useJsonB) {
return;
Expand Down