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

Scan Kotlin nullability annotations #1253

Merged
merged 2 commits into from
Sep 8, 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
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ public class KotlinConstants {
public static final DotName CONTINUATION = DotName
.createSimple("kotlin.coroutines.Continuation");

public static final DotName JETBRAINS_NULLABLE = DotName
.createSimple("org.jetbrains.annotations.Nullable");

public static final DotName JETBRAINS_NOT_NULL = DotName
.createSimple("org.jetbrains.annotations.NotNull");

private KotlinConstants() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.jboss.jandex.Type;

import io.smallrye.openapi.api.constants.JacksonConstants;
import io.smallrye.openapi.api.constants.KotlinConstants;
import io.smallrye.openapi.runtime.util.JandexUtil;
import io.smallrye.openapi.runtime.util.TypeUtil;

Expand Down Expand Up @@ -100,6 +101,10 @@ public interface RequirementHandler {
// Jackson Constraints
static final DotName JACKSON_JSONPROPERTY = createConstraintName(JacksonConstants.JSON_PROPERTY);

// Kotlin Constraints
static final DotName KOTLIN_NULLABLE = createConstraintName(KotlinConstants.JETBRAINS_NULLABLE);
static final DotName KOTLIN_NOT_NULL = createConstraintName(KotlinConstants.JETBRAINS_NOT_NULL);

static DotName createConstraintName(DotName packageName, String className) {
return createConstraintName(createComponentized(packageName, className));
}
Expand Down Expand Up @@ -175,8 +180,7 @@ public void applyConstraints(AnnotationTarget target,
applyArrayConstraints(target, schema, propertyKey, handler);
break;
case BOOLEAN:
notNull(target, schema, propertyKey, handler);
requiredJackson(target, propertyKey, handler);
applyBooleanConstraints(target, schema, propertyKey, handler);
break;
case INTEGER:
applyNumberConstraints(target, schema, propertyKey, handler);
Expand All @@ -202,7 +206,9 @@ private void applyStringConstraints(AnnotationTarget target,
pattern(target, schema);
digits(target, schema);
notBlank(target, schema, propertyKey, handler);
notNull(target, schema, propertyKey, handler);
notNull(target, propertyKey, handler);
notNullKotlin(target, propertyKey, handler);
nullableKotlin(target, schema);
requiredJackson(target, propertyKey, handler);
sizeString(target, schema);
notEmptyString(target, schema, propertyKey, handler);
Expand All @@ -212,7 +218,9 @@ private void applyObjectConstraints(AnnotationTarget target,
Schema schema,
String propertyKey,
RequirementHandler handler) {
notNull(target, schema, propertyKey, handler);
notNull(target, propertyKey, handler);
notNullKotlin(target, propertyKey, handler);
nullableKotlin(target, schema);
requiredJackson(target, propertyKey, handler);
sizeObject(target, schema);
notEmptyObject(target, schema, propertyKey, handler);
Expand All @@ -222,7 +230,9 @@ private void applyArrayConstraints(AnnotationTarget target,
Schema schema,
String propertyKey,
RequirementHandler handler) {
notNull(target, schema, propertyKey, handler);
notNull(target, propertyKey, handler);
notNullKotlin(target, propertyKey, handler);
nullableKotlin(target, schema);
requiredJackson(target, propertyKey, handler);
sizeArray(target, schema);
notEmptyArray(target, schema, propertyKey, handler);
Expand All @@ -239,12 +249,24 @@ private void applyNumberConstraints(AnnotationTarget target,
min(target, schema);
negative(target, schema);
negativeOrZero(target, schema);
notNull(target, schema, propertyKey, handler);
notNull(target, propertyKey, handler);
notNullKotlin(target, propertyKey, handler);
nullableKotlin(target, schema);
requiredJackson(target, propertyKey, handler);
positive(target, schema);
positiveOrZero(target, schema);
}

private void applyBooleanConstraints(AnnotationTarget target,
Schema schema,
String propertyKey,
RequirementHandler handler) {
notNull(target, propertyKey, handler);
notNullKotlin(target, propertyKey, handler);
nullableKotlin(target, schema);
requiredJackson(target, propertyKey, handler);
}

void decimalMax(AnnotationTarget target, Schema schema) {
AnnotationInstance constraint = getConstraint(target, BV_DECIMAL_MAX);

Expand Down Expand Up @@ -418,14 +440,26 @@ void notEmptyString(AnnotationTarget target, Schema schema, String propertyKey,
}
}

void notNull(AnnotationTarget target, Schema schema, String propertyKey, RequirementHandler handler) {
void notNull(AnnotationTarget target, String propertyKey, RequirementHandler handler) {
AnnotationInstance constraint = getConstraint(target, BV_NOT_NULL);

if (constraint != null) {
handler.setRequired(target, propertyKey);
}
}

void notNullKotlin(AnnotationTarget target, String propertyKey, RequirementHandler handler) {
if (TypeUtil.hasAnnotation(target, KOTLIN_NOT_NULL)) {
handler.setRequired(target, propertyKey);
}
}

void nullableKotlin(AnnotationTarget target, Schema schema) {
if (TypeUtil.hasAnnotation(target, KOTLIN_NULLABLE) && schema.getNullable() == null) {
schema.setNullable(Boolean.TRUE);
}
}

void pattern(AnnotationTarget target, Schema schema) {
AnnotationInstance constraint = getConstraint(target, BV_PATTERN);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ void testArrayListNotNullAndNotEmptyAndMaxItems(FieldInfo targetField) {
Schema parentSchema = new SchemaImpl();
String propertyKey = targetField.name();

testTarget.notNull(targetField, schema, propertyKey, requirementHandler(parentSchema));
testTarget.notNull(targetField, propertyKey, requirementHandler(parentSchema));
testTarget.sizeArray(targetField, schema);
testTarget.notEmptyArray(targetField, schema, propertyKey, requirementHandler(parentSchema));

Expand All @@ -177,7 +177,7 @@ void testArrayListNullableAndMinItemsAndMaxItems(FieldInfo targetField) {
Schema parentSchema = new SchemaImpl();
String propertyKey = targetField.name();

testTarget.notNull(targetField, schema, propertyKey, requirementHandlerFail());
testTarget.notNull(targetField, propertyKey, requirementHandlerFail());
testTarget.sizeArray(targetField, schema);
testTarget.notEmptyArray(targetField, schema, propertyKey, requirementHandler(parentSchema));

Expand Down Expand Up @@ -207,7 +207,7 @@ void testMapObjectNotNullAndNotEmptyAndMaxProperties(FieldInfo targetField) {
Schema parentSchema = new SchemaImpl();
String propertyKey = targetField.name();

testTarget.notNull(targetField, schema, propertyKey, requirementHandler(parentSchema));
testTarget.notNull(targetField, propertyKey, requirementHandler(parentSchema));
testTarget.sizeObject(targetField, schema);
testTarget.notEmptyObject(targetField, schema, propertyKey, requirementHandler(parentSchema));

Expand Down Expand Up @@ -235,7 +235,7 @@ void testMapObjectNullableAndMinPropertiesAndMaxProperties(FieldInfo targetField
Schema parentSchema = new SchemaImpl();
String propertyKey = targetField.name();

testTarget.notNull(targetField, schema, propertyKey, requirementHandlerFail());
testTarget.notNull(targetField, propertyKey, requirementHandlerFail());
testTarget.sizeObject(targetField, schema);
testTarget.notEmptyObject(targetField, schema, propertyKey, requirementHandler(parentSchema));

Expand All @@ -261,7 +261,7 @@ void testMapObjectNullableNoAdditionalProperties(FieldInfo targetField) {
Schema parentSchema = new SchemaImpl();
String propertyKey = targetField.name();

testTarget.notNull(targetField, schema, propertyKey, requirementHandlerFail());
testTarget.notNull(targetField, propertyKey, requirementHandlerFail());
testTarget.sizeObject(targetField, schema);
testTarget.notEmptyObject(targetField, schema, propertyKey, requirementHandlerFail());

Expand Down Expand Up @@ -646,7 +646,7 @@ void testStringNotBlankNotNull(FieldInfo targetField) {
String propertyKey = targetField.name();

testTarget.notBlank(targetField, schema, propertyKey, requirementHandler(parentSchema));
testTarget.notNull(targetField, schema, propertyKey, requirementHandler(parentSchema));
testTarget.notNull(targetField, propertyKey, requirementHandler(parentSchema));

assertEquals("\\S", schema.getPattern());
assertNull(schema.getNullable());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@
"components": {
"schemas": {
"KotlinBean": {
"required": [
"description"
],
"type": "object",
"properties": {
"id": {
"format": "int64",
"type": "integer"
"type": "integer",
"nullable": true
},
"name": {
"type": "string",
"nullable": true
},
"description": {
"type": "string"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ import org.eclipse.microprofile.openapi.annotations.media.Schema
data class KotlinBean (
@field:Schema(type = SchemaType.INTEGER, implementation = Long::class, name = "id")
val id: KotlinLongValue? = null,
val name: String? = null
val name: String? = null,
val description: String = ""
)