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

Ignore hidden schema properties, add support for JSON-B name override #91

Merged
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
11 changes: 8 additions & 3 deletions implementation/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@
<groupId>org.eclipse.microprofile.openapi</groupId>
<artifactId>microprofile-openapi-api</artifactId>
</dependency>

<!-- MP Config Spec -->
<dependency>
<groupId>org.eclipse.microprofile.config</groupId>
<artifactId>microprofile-config-api</artifactId>
</dependency>

<!-- Third Party Libraries -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
Expand Down Expand Up @@ -83,7 +83,7 @@
<artifactId>validation-api</artifactId>
<scope>provided</scope>
</dependency>

<!-- Test Only Dependencies -->
<dependency>
<groupId>junit</groupId>
Expand All @@ -95,6 +95,11 @@
<artifactId>jsonassert</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.json.bind</groupId>
<artifactId>javax.json.bind-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.microprofile.openapi</groupId>
<artifactId>microprofile-openapi-tck</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ public final class OpenApiConstants {

public static final DotName COMPLETION_STAGE_NAME = DotName.createSimple(CompletionStage.class.getName());

public static final DotName DOTNAME_JSONB_PROPERTY = DotName.createSimple("javax.json.bind.annotation.JsonbProperty");
public static final DotName DOTNAME_JSONB_TRANSIENT = DotName.createSimple("javax.json.bind.annotation.JsonbTransient");

public static final String[] DEFAULT_CONSUMES = new String[] {MIME_ANY};
public static final String[] DEFAULT_PRODUCES = new String[] {MIME_ANY};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,34 +105,48 @@ public static Schema process(AugmentedIndexView index,
public Schema processField() {
AnnotationInstance schemaAnnotation = TypeUtil.getSchemaAnnotation(annotationTarget);

final String propertyKey = readPropertyKey();

if (schemaAnnotation == null && shouldInferUnannotatedFields()) {
// Handle unannotated field and just do simple inference.
readUnannotatedField();
} else {
// Handle field annotated with @Schema.
readSchemaAnnotatedField(schemaAnnotation);
readSchemaAnnotatedField(propertyKey, schemaAnnotation);
}
parentPathEntry.getSchema().addProperty(entityName, fieldSchema);
parentPathEntry.getSchema().addProperty(propertyKey, fieldSchema);
return fieldSchema;
}

private void readSchemaAnnotatedField(@NotNull AnnotationInstance annotation) {
if (annotation == null) {
throw new IllegalArgumentException("Annotation must not be null");
private String readPropertyKey() {
AnnotationInstance jsonbAnnotation = TypeUtil.getAnnotation(annotationTarget,
OpenApiConstants.DOTNAME_JSONB_PROPERTY);
String key;

if (jsonbAnnotation != null) {
key = JandexUtil.stringValue(jsonbAnnotation, OpenApiConstants.PROP_VALUE);

if (key == null) {
key = entityName;
}
} else {
key = entityName;
}

LOG.debugv("Processing @Schema annotation {0} on a field {1}", annotation, entityName);
return key;
}

// Schemas can be hidden. Skip if that's the case.
Boolean isHidden = JandexUtil.booleanValue(annotation, OpenApiConstants.PROP_HIDDEN);
if (isHidden != null && isHidden == Boolean.TRUE) {
return;
private void readSchemaAnnotatedField(String propertyKey, @NotNull AnnotationInstance annotation) {
if (annotation == null) {
throw new IllegalArgumentException("Annotation must not be null");
}

LOG.debugv("Processing @Schema annotation {0} on a field {1}", annotation, propertyKey);

// If "required" attribute is on field. It should be applied to the *parent* schema.
// Required is false by default.
if (JandexUtil.booleanValueWithDefault(annotation, OpenApiConstants.PROP_REQUIRED)) {
parentPathEntry.getSchema().addRequired(entityName);
parentPathEntry.getSchema().addRequired(propertyKey);
}

// Type could be replaced (e.g. generics).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonIgnoreType;

import io.smallrye.openapi.api.OpenApiConstants;
import io.smallrye.openapi.runtime.scanner.dataobject.DataObjectDeque.PathEntry;
import io.smallrye.openapi.runtime.util.JandexUtil;
import io.smallrye.openapi.runtime.util.TypeUtil;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationValue;
Expand Down Expand Up @@ -46,6 +51,8 @@ public class IgnoreResolver {

{
IgnoreAnnotationHandler[] ignoreHandlers = {
new SchemaHiddenHandler(),
new JsonbTransientHandler(),
new JsonIgnorePropertiesHandler(),
new JsonIgnoreHandler(),
new JsonIgnoreTypeHandler()
Expand All @@ -70,6 +77,52 @@ public boolean isIgnore(AnnotationTarget annotationTarget, DataObjectDeque.PathE
return false;
}

/**
* Handler for OAS hidden @{@link Schema}
*/
private final class SchemaHiddenHandler implements IgnoreAnnotationHandler {
@Override
public boolean shouldIgnore(AnnotationTarget target, PathEntry parentPathEntry) {
AnnotationInstance annotationInstance = TypeUtil.getAnnotation(target, getName());

if (annotationInstance != null) {
Boolean isHidden = JandexUtil.booleanValue(annotationInstance,
OpenApiConstants.PROP_HIDDEN);

if (isHidden != null) {
return isHidden;
}
}

return false;
}

@Override
public DotName getName() {
return OpenApiConstants.DOTNAME_SCHEMA;
}
}

/**
* Handler for JSON-B's @{@link javax.json.bind.annotation.JsonbTransient}
*/
private final class JsonbTransientHandler implements IgnoreAnnotationHandler {

@Override
public boolean shouldIgnore(AnnotationTarget target, DataObjectDeque.PathEntry parentPathEntry) {
AnnotationInstance annotationInstance = TypeUtil.getAnnotation(target, getName());
if (annotationInstance != null) {
return true;
}
return false;
}

@Override
public DotName getName() {
return OpenApiConstants.DOTNAME_JSONB_TRANSIENT;
}
}

/**
* Handler for Jackson's {@link JsonIgnoreProperties}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,15 @@ public void genericFieldTest() throws IOException, JSONException {
assertJsonEquals(name, "generic.fields.expected.json", result);
}

@Test
public void fieldNameOverrideTest() throws IOException, JSONException {
String name = GenericTypeTestContainer.class.getName();
Type pType = getFieldFromKlazz(name, "overriddenNames").type();
OpenApiDataObjectScanner scanner = new OpenApiDataObjectScanner(index, pType);

Schema result = scanner.process();

printToConsole(name, result);
assertJsonEquals(name, "generic.fields.overriddenNames.expected.json", result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
import org.jboss.jandex.Type;
import org.json.JSONException;
import org.junit.Test;

import test.io.smallrye.openapi.runtime.scanner.entities.IgnoreSchemaOnFieldExample;
import test.io.smallrye.openapi.runtime.scanner.entities.IgnoreTestContainer;
import test.io.smallrye.openapi.runtime.scanner.entities.JsonIgnoreOnFieldExample;
import test.io.smallrye.openapi.runtime.scanner.entities.JsonIgnoreTypeExample;
import test.io.smallrye.openapi.runtime.scanner.entities.JsonbTransientOnFieldExample;

import java.io.IOException;

Expand Down Expand Up @@ -85,4 +88,30 @@ public void testIgnore_jsonIgnoreType() throws IOException, JSONException {
assertJsonEquals(name.local(), "ignore.jsonIgnoreType.expected.json", result);
}

// Entirely ignore a single field once using JSON-B.
@Test
public void testIgnore_jsonbTransientField() throws IOException, JSONException {
DotName name = DotName.createSimple(JsonbTransientOnFieldExample.class.getName());
OpenApiDataObjectScanner scanner = new OpenApiDataObjectScanner(index,
ClassType.create(name, Type.Kind.CLASS));

Schema result = scanner.process();

printToConsole(name.local(), result);
assertJsonEquals(name.local(), "ignore.jsonbTransientField.expected.json", result);
}

// Entirely ignore a single field once using hidden attribute of Schema.
@Test
public void testIgnore_schemaHiddenField() throws IOException, JSONException {
DotName name = DotName.createSimple(IgnoreSchemaOnFieldExample.class.getName());
OpenApiDataObjectScanner scanner = new OpenApiDataObjectScanner(index,
ClassType.create(name, Type.Kind.CLASS));

Schema result = scanner.process();

printToConsole(name.local(), result);
assertJsonEquals(name.local(), "ignore.schemaHiddenField.expected.json", result);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2018 Red Hat, Inc, and individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package test.io.smallrye.openapi.runtime.scanner.entities;

import javax.json.bind.annotation.JsonbProperty;

import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
* @author Michael Edgar {@literal <michael@xlate.io>}
*/
public class FieldNameOverride {

@JsonbProperty("dasherized-name")
private String dasherizedName;

@JsonbProperty("dasherized-name-required")
@Schema(required = true)
private String dasherizedNameRequired;

@JsonbProperty("snake_case_name")
private String snakeCaseName;

@JsonbProperty("camelCaseNameCustom")
private String camelCaseName;

@JsonbProperty
private String camelCaseNameDefault1;

@SuppressWarnings("unused")
private String camelCaseNameDefault2;

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ public class GenericTypeTestContainer {

// Type containing a variety of collections and maps.
GenericFieldTestContainer<String, LocalDateTime> genericContainer;

// Type containing fields with overridden names.
FieldNameOverride overriddenNames;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package test.io.smallrye.openapi.runtime.scanner.entities;

import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
* @author Michael Edgar {@literal <michael@xlate.io>}
*/

public class IgnoreSchemaOnFieldExample {
@Schema(hidden = true)
String ignoredField;

@Schema(hidden = false, description = "This field is not hidden")
String serializedField1;

@Schema(description = "This field is not hidden either")
String serializedField2;

String serializedField3;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package test.io.smallrye.openapi.runtime.scanner.entities;

import javax.json.bind.annotation.JsonbTransient;

/**
* @author Michael Edgar {@literal <michael@xlate.io>}
*/

public class JsonbTransientOnFieldExample {

@JsonbTransient
String ignoredField;

String serializedField;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"components" : {
"schemas" : {
"test.io.smallrye.openapi.runtime.scanner.entities.GenericTypeTestContainer" : {
"required" : [
"dasherized-name-required"
],
"properties" : {
"dasherized-name" : {
"type" : "string"
},
"dasherized-name-required" : {
"type" : "string"
},
"snake_case_name" : {
"type" : "string"
},
"camelCaseNameCustom" : {
"type" : "string"
},
"camelCaseNameDefault1" : {
"type" : "string"
},
"camelCaseNameDefault2" : {
"type" : "string"
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"components" : {
"schemas" : {
"test.io.smallrye.openapi.runtime.scanner.entities.JsonbTransientOnFieldExample" : {
"properties" : {
"serializedField" : {
"type" : "string"
}
}
}
}
}
}
Loading