Skip to content

Commit

Permalink
Merge pull request #1112 from altro3/json_view
Browse files Browse the repository at this point in the history
Add support for @JSONVIEW
  • Loading branch information
altro3 authored Jul 15, 2023
2 parents a73827d + 7ab960d commit cc796ab
Show file tree
Hide file tree
Showing 6 changed files with 543 additions and 166 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -260,10 +260,18 @@ public class OpenApiApplicationVisitor extends AbstractOpenApiVisitor implements
* Final calculated openapi filenames.
*/
public static final String MICRONAUT_INTERNAL_OPENAPI_FILENAMES = "micronaut.internal.openapi.filenames";
/**
* Loaded micronaut-http-server-netty property (json-view.enabled).
*/
public static final String MICRONAUT_JACKSON_JSON_VIEW_ENABLED = "jackson.json-view.enabled";
/**
* Loaded micronaut environment.
*/
private static final String MICRONAUT_ENVIRONMENT = "micronaut.environment";
/**
* Loaded into context jackson.json-view.enabled property value.
*/
private static final String MICRONAUT_INTERNAL_JACKSON_JSON_VIEW_ENABLED = "micronaut.internal.jackson.json-view.enabled";
private static final String MICRONAUT_ENVIRONMENT_CREATED = "micronaut.environment.created";
private static final String MICRONAUT_OPENAPI_PROPERTIES = "micronaut.openapi.properties";
private static final String MICRONAUT_OPENAPI_ENDPOINTS = "micronaut.openapi.endpoints";
Expand Down Expand Up @@ -618,6 +626,19 @@ public static String getConfigurationProperty(String key, VisitorContext context
return environment != null ? environment.get(key, String.class).orElse(null) : null;
}

public static boolean isJsonViewEnabled(VisitorContext context) {

Boolean isJsonViewEnabled = context.get(MICRONAUT_INTERNAL_JACKSON_JSON_VIEW_ENABLED, Boolean.class).orElse(null);
if (isJsonViewEnabled != null) {
return isJsonViewEnabled;
}

isJsonViewEnabled = getBooleanProperty(MICRONAUT_JACKSON_JSON_VIEW_ENABLED, false, context);
context.put(MICRONAUT_INTERNAL_JACKSON_JSON_VIEW_ENABLED, isJsonViewEnabled);

return isJsonViewEnabled;
}

public static SecurityProperties getSecurityProperties(VisitorContext context) {

SecurityProperties securityProperties = context.get(MICRONAUT_INTERNAL_SECURITY_PROPERTIES, SecurityProperties.class).orElse(null);
Expand Down Expand Up @@ -1070,7 +1091,7 @@ private void copyOpenApi(OpenAPI to, OpenAPI from) {

private OpenAPI readOpenApi(ClassElement element, VisitorContext context) {
return element.findAnnotation(OpenAPIDefinition.class).flatMap(o -> {
Optional<OpenAPI> result = toValue(o.getValues(), context, OpenAPI.class);
Optional<OpenAPI> result = toValue(o.getValues(), context, OpenAPI.class, null);
result.ifPresent(openAPI -> {
List<io.swagger.v3.oas.models.security.SecurityRequirement> securityRequirements =
o.getAnnotations("security", SecurityRequirement.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import io.micronaut.inject.visitor.TypeElementVisitor;
import io.micronaut.inject.visitor.VisitorContext;
import io.micronaut.openapi.annotation.OpenAPIGroupInfo;
import io.micronaut.openapi.annotation.OpenAPIInclude;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.models.OpenAPI;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,17 +457,6 @@ public static Link mergeLink(Link l1, Link l2) {
l1.setExtensions(l2.getExtensions());
}

if (CollectionUtils.isEmpty(l1.getHeaders())) {
l1.setHeaders(l2.getHeaders());
} else if (CollectionUtils.isNotEmpty(l2.getHeaders())) {
for (Map.Entry<String, Header> entry1 : l1.getHeaders().entrySet()) {
Header h2 = l2.getHeaders().get(entry1.getKey());
entry1.setValue(mergeHeader(entry1.getValue(), h2));
}
l2.getHeaders().putAll(l1.getHeaders());
l1.setHeaders(l2.getHeaders());
}

return l1;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
package io.micronaut.openapi.visitor

import io.micronaut.openapi.AbstractOpenApiTypeElementSpec
import io.swagger.v3.oas.models.OpenAPI
import io.swagger.v3.oas.models.Operation
import io.swagger.v3.oas.models.media.Schema

class OpenApiJsonViewSpec extends AbstractOpenApiTypeElementSpec {

void "test build OpenAPI with JsonView"() {

setup:
System.setProperty(OpenApiApplicationVisitor.MICRONAUT_JACKSON_JSON_VIEW_ENABLED, "true")

when:
buildBeanDefinition('test.MyBean', '''
package test;
import java.util.List;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import com.fasterxml.jackson.annotation.JsonView;
@Controller
class OpenApiController {
@Get("/summary")
@JsonView(View.Summary.class)
@Operation(summary = "Return car summaries",
responses = @ApiResponse(responseCode = "200", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Car.class)))))
public HttpResponse<?> getSummaries() {
return null;
}
@Get("/detail")
@JsonView(View.Detail.class)
@ApiResponse(responseCode = "200", description = "Return car detail", content = @Content(schema = @Schema(implementation = Car.class)))
@Operation(summary = "Return car detail")
public List<Car> getDetails() {
return null;
}
/**
* {@summary Return car sale summary}
*/
@Get("/sale")
@JsonView(View.Sale.class)
public List<Car> getSaleSummaries() {
return null;
}
@Post("/add")
public void addCar(@JsonView(View.Sale.class) @Body Car car) {
}
}
interface View {
interface Summary {}
interface Detail extends Summary {}
interface Sale {}
}
class Car {
@JsonView(View.Summary.class)
private String made;
@JsonView({View.Summary.class, View.Detail.class})
private String model;
@JsonView(View.Detail.class)
private List<Tire> tires;
@JsonView(View.Sale.class)
private int price;
@JsonView({View.Sale.class, View.Summary.class})
private int age;
// common
private String color;
public String getColor() {
return color;
}
public String getMade() {
return made;
}
public void setMade(String made) {
this.made = made;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public List<Tire> getTires() {
return tires;
}
public void setTires(List<Tire> tires) {
this.tires = tires;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setColor(String color) {
this.color = color;
}
}
class Tire {
@JsonView(View.Summary.class)
private String made;
@JsonView(View.Detail.class)
private String condition;
public String getMade() {
return made;
}
public void setMade(String made) {
this.made = made;
}
public String getCondition() {
return condition;
}
public void setCondition(String condition) {
this.condition = condition;
}
}
@jakarta.inject.Singleton
class MyBean {}
''')
then: "the state is correct"
Utils.testReference != null

when: "The OpenAPI is retrieved"
OpenAPI openAPI = Utils.testReference
Schema carDetail = openAPI.components.schemas['Car_Detail']
Schema carSale = openAPI.components.schemas['Car_Sale']
Schema carSummary = openAPI.components.schemas['Car_Summary']
Schema tireDetail = openAPI.components.schemas['Tire_Detail']
Operation addOp = openAPI.paths."/add".post
Operation detailOp = openAPI.paths."/detail".get
Operation saleOp = openAPI.paths."/sale".get
Operation summaryOp = openAPI.paths."/summary".get

then:

addOp
addOp.requestBody.content.'application/json'.schema.$ref == '#/components/schemas/Car_Sale'

detailOp
detailOp.responses.'200'.content.'application/json'.schema.$ref == '#/components/schemas/Car_Detail'

saleOp
saleOp.responses.'200'.content.'application/json'.schema.items.$ref == '#/components/schemas/Car_Sale'

summaryOp
summaryOp.responses.'200'.content.'application/json'.schema.items.$ref == '#/components/schemas/Car_Summary'

carDetail
carDetail.properties.size() == 5
carDetail.properties.color
carDetail.properties.made
carDetail.properties.model
carDetail.properties.tires
carDetail.properties.age

carSale
carSale.properties.size() == 3
carSale.properties.color
carSale.properties.price
carSale.properties.age

carSummary
carSummary.properties.size() == 4
carSummary.properties.color
carSummary.properties.made
carSummary.properties.model
carSummary.properties.age

tireDetail
tireDetail.properties.size() == 2
tireDetail.properties.made
tireDetail.properties.condition

cleanup:
System.clearProperty(OpenApiApplicationVisitor.MICRONAUT_JACKSON_JSON_VIEW_ENABLED)
}
}

0 comments on commit cc796ab

Please sign in to comment.