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 support for @JsonView #1112

Merged
merged 1 commit into from
Jul 15, 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

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