Skip to content

Commit dd25ad5

Browse files
Fix handling of Optional<T> in swagger/OpenAPI specs. (#23804)
1 parent 4eaf00c commit dd25ad5

File tree

4 files changed

+85
-2
lines changed

4 files changed

+85
-2
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (C) 2020 Graylog, Inc.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the Server Side Public License, version 1,
6+
* as published by MongoDB, Inc.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* Server Side Public License for more details.
12+
*
13+
* You should have received a copy of the Server Side Public License
14+
* along with this program. If not, see
15+
* <http://www.mongodb.com/licensing/server-side-public-license>.
16+
*/
17+
package org.graylog2.shared.rest.documentation.generator;
18+
19+
import com.fasterxml.jackson.databind.JavaType;
20+
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor;
21+
import com.fasterxml.jackson.module.jsonSchema.jakarta.factories.ObjectVisitor;
22+
import com.fasterxml.jackson.module.jsonSchema.jakarta.factories.SchemaFactoryWrapper;
23+
import com.fasterxml.jackson.module.jsonSchema.jakarta.factories.VisitorContext;
24+
import com.fasterxml.jackson.module.jsonSchema.jakarta.types.ObjectSchema;
25+
26+
public class CustomSchemaFactoryWrapper extends SchemaFactoryWrapper {
27+
@Override
28+
public JsonObjectFormatVisitor expectObjectFormat(JavaType convertedType) {
29+
ObjectSchema s = new ObjectSchemaWithOptionalSupport();
30+
schema = s;
31+
32+
// if we don't already have a recursive visitor context, create one
33+
if (visitorContext == null) {
34+
visitorContext = new VisitorContext();
35+
}
36+
37+
// give each object schema a reference id and keep track of the ones we've seen
38+
String schemaUri = visitorContext.addSeenSchemaUri(convertedType);
39+
if (schemaUri != null) {
40+
s.setId(schemaUri);
41+
}
42+
43+
final var result = visitorFactory.objectFormatVisitor(provider, s);
44+
((ObjectVisitor) result).setVisitorContext(visitorContext);
45+
return result;
46+
}
47+
}

graylog2-server/src/main/java/org/graylog2/shared/rest/documentation/generator/Generator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.fasterxml.jackson.annotation.JsonValue;
2020
import com.fasterxml.jackson.databind.ObjectMapper;
21+
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
2122
import com.fasterxml.jackson.module.jsonSchema.jakarta.JsonSchema;
2223
import com.fasterxml.jackson.module.jsonSchema.jakarta.JsonSchemaGenerator;
2324
import com.fasterxml.jackson.module.jsonSchema.jakarta.factories.SchemaFactoryWrapper;
@@ -123,7 +124,7 @@ public Generator(Set<Class<?>> resourceClasses,
123124
this.resourceClasses = resourceClasses;
124125
this.pluginMapping = pluginMapping;
125126
this.pluginPathPrefix = pluginPathPrefix;
126-
this.mapper = mapper;
127+
this.mapper = mapper.copy().registerModule(new Jdk8Module());
127128
this.isCloud = isCloud;
128129
this.prefixPlugins = prefixPlugins;
129130
}
@@ -631,7 +632,7 @@ private static boolean isObjectSchema(Map<String, Object> genericTypeSchema) {
631632
}
632633

633634
private Map<String, Object> schemaForType(Type valueType) {
634-
final SchemaFactoryWrapper schemaFactoryWrapper = new SchemaFactoryWrapper() {};
635+
final SchemaFactoryWrapper schemaFactoryWrapper = new CustomSchemaFactoryWrapper();
635636
final JsonSchemaGenerator schemaGenerator = new JsonSchemaGenerator(mapper, schemaFactoryWrapper);
636637
try {
637638
final JsonSchema schema = schemaGenerator.generateSchema(mapper.getTypeFactory().constructType(valueType));
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (C) 2020 Graylog, Inc.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the Server Side Public License, version 1,
6+
* as published by MongoDB, Inc.
7+
*
8+
* This program is distributed in the hope that it will be useful,
9+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
* Server Side Public License for more details.
12+
*
13+
* You should have received a copy of the Server Side Public License
14+
* along with this program. If not, see
15+
* <http://www.mongodb.com/licensing/server-side-public-license>.
16+
*/
17+
package org.graylog2.shared.rest.documentation.generator;
18+
19+
import com.fasterxml.jackson.databind.BeanProperty;
20+
import com.fasterxml.jackson.module.jsonSchema.jakarta.JsonSchema;
21+
import com.fasterxml.jackson.module.jsonSchema.jakarta.types.ObjectSchema;
22+
23+
import java.util.Optional;
24+
25+
public class ObjectSchemaWithOptionalSupport extends ObjectSchema {
26+
@Override
27+
public void putOptionalProperty(BeanProperty property, JsonSchema jsonSchema) {
28+
if (property.getType().isTypeOrSubTypeOf(Optional.class)) {
29+
jsonSchema.setRequired(false);
30+
}
31+
super.putOptionalProperty(property, jsonSchema);
32+
}
33+
}

graylog2-server/src/test/java/org/graylog2/rest/documentation/generator/GeneratorTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ public void testInnerClasses() throws Exception {
101101
assertThat(jsonResult.read("$.apis[0].operations[0].nickname", String.class)).isEqualTo("sample");
102102

103103
assertThat(jsonResult.read("$.models.GeneratorTest__SampleEntity.properties.type.type", String.class)).isEqualTo("string");
104+
assertThat(jsonResult.read("$.models.GeneratorTest__SampleResponse.properties.foo.type", String.class)).isEqualTo("string");
105+
assertThat(jsonResult.read("$.models.GeneratorTest__SampleResponse.properties.foo.required", String.class)).isEqualTo("false");
104106
assertThat(jsonResult.read("$.models.GeneratorTest__SampleResponse.properties.entity[\"$ref\"]", String.class)).isEqualTo("GeneratorTest__SampleEntity");
105107
assertThat(jsonResult.read("$.models.GeneratorTest__SampleResponse.properties.another_entity[\"$ref\"]", String.class)).isEqualTo("GeneratorTest__SampleEntity");
106108
}

0 commit comments

Comments
 (0)