Skip to content

(Javalin) Avaje JsonB support #99

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

Merged
merged 19 commits into from
Oct 20, 2022
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ build/
*.project
*.processors
*/bin/
*.editorconfig
*.Module
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ Javalin.create()
.start();
```


## Usage with Helidon SE

The annotation processor will generate controller classes implementing the Helidon Service interface, which we can use
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package io.avaje.http.generator.client;

import java.util.List;

import io.avaje.http.generator.core.Append;
import io.avaje.http.generator.core.ControllerReader;
import io.avaje.http.generator.core.ParamType;
import io.avaje.http.generator.core.PlatformAdapter;

import java.util.List;
import io.avaje.http.generator.core.UType;

class ClientPlatformAdapter implements PlatformAdapter {

Expand All @@ -20,7 +21,7 @@ public String platformVariable(String rawType) {
}

@Override
public String bodyAsClass(String shortType) {
public String bodyAsClass(UType uType) {
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ void writeCtxGet(Append writer, PathSegments segments) {
return;
}
String shortType = shortType();
writer.append("%s %s %s = ", ctx.platform().indent(), shortType, varName);
writer.append("%s var %s = ", ctx.platform().indent(), varName);
if (setValue(writer, segments, shortType)) {
writer.append(";").eol();
}
Expand Down Expand Up @@ -259,7 +259,7 @@ private boolean setValue(Append writer, PathSegments segments, String shortType)

if (typeHandler == null) {
// this is a body (POST, PATCH)
writer.append(ctx.platform().bodyAsClass(shortType));
writer.append(ctx.platform().bodyAsClass(type));

} else {
if (hasParamDefault()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package io.avaje.http.generator.core;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;

public class JsonBUtil {
private JsonBUtil() {}

public static Map<String, UType> getJsonTypes(ControllerReader reader) {

final Map<String, UType> jsonTypes = new LinkedHashMap<>();

final Consumer<UType> addToMap = uType -> jsonTypes.put(uType.full(), uType);

reader.getMethods().stream()
.filter(MethodReader::isWebMethod)
.filter(m -> !"byte[]".equals(m.getReturnType().toString()))
.filter(m -> m.getProduces() == null || m.getProduces().toLowerCase().contains("json"))
.forEach(
methodReader -> {
addJsonBodyType(methodReader, addToMap);
if (!methodReader.isVoid()) {
addToMap.accept(UType.parse(methodReader.getReturnType()));
}
});

return Map.copyOf(jsonTypes);
}

private static void addJsonBodyType(MethodReader methodReader, Consumer<UType> addToMap) {
if (methodReader.getBodyType() != null) {
methodReader.getParams().stream()
.filter(MethodParam::isBody)
.map(MethodParam::getUType)
.forEach(addToMap);
}
}

public static void writeJsonbType(UType type, Append writer) {

writer.append(" this.%sJsonType = jsonB.type(", type.shortName());
if (!type.isGeneric()) {
writer.append("%s.class)", PrimitiveUtil.wrap(type.full()));
} else {
switch (type.mainType()) {
case "java.util.List":
writer.append("%s.class).list()", type.param0());
break;
case "java.util.Set":
writer.append("%s.class).set()", type.param0());
break;
case "java.util.Map":
writer.append("%s.class).map()", type.param1());
break;
default:
throw new UnsupportedOperationException(
"Only java.util Map, Set and List are supported JsonB Controller Collection Types");
}
}
writer.append(";").eol();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public interface PlatformAdapter {
/**
* Return platform specific code to return the body content.
*/
String bodyAsClass(String shortType);
String bodyAsClass(UType type);

/**
* Return true if body is passed as a method parameter.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.avaje.http.generator.core;

import java.util.Map;

public final class PrimitiveUtil {

private PrimitiveUtil() {}

static final Map<String, String> wrapperMap =
Map.of(
"char", "Character",
"byte", "Byte",
"int", "Integer",
"long", "Long",
"short", "Short",
"double", "Double",
"float", "Float",
"boolean", "Boolean");

public static String wrap(String shortName) {
final var wrapped = wrapperMap.get(shortName);
return wrapped != null ? wrapped : shortName;
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package io.avaje.http.generator.helidon;

import java.util.List;

import io.avaje.http.generator.core.Append;
import io.avaje.http.generator.core.ControllerReader;
import io.avaje.http.generator.core.ParamType;
import io.avaje.http.generator.core.PlatformAdapter;

import java.util.List;
import io.avaje.http.generator.core.UType;

class HelidonPlatformAdapter implements PlatformAdapter {

Expand Down Expand Up @@ -38,7 +39,7 @@ public boolean isBodyMethodParam() {
}

@Override
public String bodyAsClass(String shortType) {
public String bodyAsClass(UType uType) {
return "body";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,53 +6,56 @@
import io.avaje.http.generator.core.MethodReader;
import io.avaje.http.generator.core.PathSegments;
import io.avaje.http.generator.core.ProcessingContext;
import io.avaje.http.generator.core.UType;
import io.avaje.http.generator.core.Util;
import io.avaje.http.generator.core.WebMethod;

import java.util.List;

/**
* Write code to register Web route for a given controller method.
*/
/** Write code to register Web route for a given controller method. */
class ControllerMethodWriter {

private final MethodReader method;
private final Append writer;
private final WebMethod webMethod;
private final ProcessingContext ctx;
private final boolean useJsonB;

ControllerMethodWriter(MethodReader method, Append writer, ProcessingContext ctx) {
ControllerMethodWriter(
MethodReader method, Append writer, ProcessingContext ctx, boolean useJsonB) {
this.method = method;
this.writer = writer;
this.webMethod = method.getWebMethod();
this.ctx = ctx;
this.useJsonB = useJsonB;
}

void write(boolean requestScoped) {

final PathSegments segments = method.getPathSegments();
final String fullPath = segments.fullPath();
final var segments = method.getPathSegments();
final var fullPath = segments.fullPath();

writer.append(" ApiBuilder.%s(\"%s\", ctx -> {", webMethod.name().toLowerCase(), fullPath).eol();
writer
.append(" ApiBuilder.%s(\"%s\", ctx -> {", webMethod.name().toLowerCase(), fullPath)
.eol();
writer.append(" ctx.status(%s);", method.getStatusCode()).eol();

List<PathSegments.Segment> matrixSegments = segments.matrixSegments();
for (PathSegments.Segment matrixSegment : matrixSegments) {
final var matrixSegments = segments.matrixSegments();
for (final PathSegments.Segment matrixSegment : matrixSegments) {
matrixSegment.writeCreateSegment(writer, ctx.platform());
}

final List<MethodParam> params = method.getParams();
for (MethodParam param : params) {
final var params = method.getParams();
for (final MethodParam param : params) {
param.writeCtxGet(writer, segments);
}
writer.append(" ");
if (method.includeValidate()) {
for (MethodParam param : params) {
for (final MethodParam param : params) {
param.writeValidate(writer);
}
}

if (!method.isVoid()) {
writeContextReturn();
writer.append("var result = ");
}

if (requestScoped) {
Expand All @@ -61,23 +64,25 @@ void write(boolean requestScoped) {
writer.append("controller.");
}
writer.append(method.simpleName()).append("(");
for (int i = 0; i < params.size(); i++) {
for (var i = 0; i < params.size(); i++) {
if (i > 0) {
writer.append(", ");
}
params.get(i).buildParamName(writer);
}
writer.append(")");

writer.append(");").eol();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to check this

if (!method.isVoid()) {
writer.append(")");
writeContextReturn();
writer.eol();
}
writer.append(";").eol();

writer.append(" }");

List<String> roles = method.roles();
final var roles = method.roles();
if (!roles.isEmpty()) {
writer.append(", ");
for (int i = 0; i < roles.size(); i++) {
for (var i = 0; i < roles.size(); i++) {
if (i > 0) {
writer.append(", ");
}
Expand All @@ -88,15 +93,23 @@ void write(boolean requestScoped) {
}

private void writeContextReturn() {
final String produces = method.getProduces();
if (produces == null || produces.equalsIgnoreCase(MediaType.APPLICATION_JSON)) {
writer.append("ctx.json(");
} else if (produces.equalsIgnoreCase(MediaType.TEXT_HTML)) {
writer.append("ctx.html(");
} else if (produces.equalsIgnoreCase(MediaType.TEXT_PLAIN)) {
writer.append("ctx.contentType(\"text/plain\").result(");
final var produces = method.getProduces();
if (produces == null || MediaType.APPLICATION_JSON.equalsIgnoreCase(produces)) {
if (useJsonB) {
final var uType = UType.parse(method.getReturnType());
writer.append(
" %sJsonType.toJson(result, ctx.contentType(\"application/json\").outputStream());",
uType.shortName());

} else {
writer.append(" ctx.json(result);");
}
} else if (MediaType.TEXT_HTML.equalsIgnoreCase(produces)) {
writer.append(" ctx.html(result);");
} else if (MediaType.TEXT_PLAIN.equalsIgnoreCase(produces)) {
writer.append(" ctx.contentType(\"text/plain\").result(result);");
} else {
writer.append("ctx.contentType(\"%s\").result(", produces);
writer.append(" ctx.contentType(\"%s\").result(result);", produces);
}
}
}
Loading