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

enhanced support for additionalProperties #4305

Merged
merged 1 commit into from
Nov 14, 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
@@ -0,0 +1,38 @@
package io.swagger.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ResolvableSerializer;
import io.swagger.models.Model;

import java.io.IOException;

public class ModelSerializer extends JsonSerializer<Model> implements ResolvableSerializer {

private JsonSerializer<Object> defaultSerializer;

public ModelSerializer(JsonSerializer<Object> serializer) {
defaultSerializer = serializer;
}

@Override
public void resolve(SerializerProvider serializerProvider) throws JsonMappingException {
if (defaultSerializer instanceof ResolvableSerializer) {
((ResolvableSerializer) defaultSerializer).resolve(serializerProvider);
}
}

@Override
public void serialize(
Model value, JsonGenerator jgen, SerializerProvider provider)
throws IOException {

if (value.getBooleanValue() != null) {
jgen.writeBoolean(value.getBooleanValue());
} else {
defaultSerializer.serialize(value, jgen, provider);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.swagger.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ResolvableSerializer;
import io.swagger.models.properties.Property;

import java.io.IOException;

public class PropertySerializer extends JsonSerializer<Property> implements ResolvableSerializer {

private JsonSerializer<Object> defaultSerializer;

public PropertySerializer(JsonSerializer<Object> serializer) {
defaultSerializer = serializer;
}

@Override
public void resolve(SerializerProvider serializerProvider) throws JsonMappingException {
if (defaultSerializer instanceof ResolvableSerializer) {
((ResolvableSerializer) defaultSerializer).resolve(serializerProvider);
}
}

@Override
public void serialize(
Property value, JsonGenerator jgen, SerializerProvider provider)
throws IOException {

if (value.getBooleanValue() != null) {
jgen.writeBoolean(value.getBooleanValue());
} else {
defaultSerializer.serialize(value, jgen, provider);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.TextNode;
import io.swagger.models.ArrayModel;
import io.swagger.models.BooleanValueModel;
import io.swagger.models.ComposedModel;
import io.swagger.models.Model;
import io.swagger.models.ModelImpl;
Expand All @@ -21,6 +22,9 @@ public class ModelDeserializer extends JsonDeserializer<Model> {
public Model deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
if (node.isBoolean()) {
return new BooleanValueModel(node.asBoolean());
}
JsonNode sub = node.get("$ref");
JsonNode allOf = node.get("allOf");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,24 @@

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.swagger.jackson.ModelSerializer;
import io.swagger.jackson.PropertySerializer;
import io.swagger.jackson.mixin.OperationResponseMixin;
import io.swagger.jackson.mixin.ResponseSchemaMixin;
import io.swagger.models.Model;
import io.swagger.models.Operation;
import io.swagger.models.Response;
import io.swagger.models.properties.Property;

public class ObjectMapperFactory {

Expand All @@ -33,6 +42,25 @@ protected static ObjectMapper createYaml(boolean includePathDeserializer, boolea
private static ObjectMapper create(JsonFactory jsonFactory, boolean includePathDeserializer, boolean includeResponseDeserializer) {
ObjectMapper mapper = jsonFactory == null ? new ObjectMapper() : new ObjectMapper(jsonFactory);

mapper.registerModule(new SimpleModule() {
@Override
public void setupModule(SetupContext context) {
super.setupModule(context);
context.addBeanSerializerModifier(new BeanSerializerModifier() {
@Override
public JsonSerializer<?> modifySerializer(
SerializationConfig config, BeanDescription desc, JsonSerializer<?> serializer) {
if (Property.class.isAssignableFrom(desc.getBeanClass())) {
return new PropertySerializer((JsonSerializer<Object>) serializer);
} else if (Model.class.isAssignableFrom(desc.getBeanClass())) {
return new ModelSerializer((JsonSerializer<Object>) serializer);
}
return serializer;
}
});
}
});

Module deserializerModule = new DeserializationModule(includePathDeserializer, includeResponseDeserializer);
mapper.registerModule(deserializerModule);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import com.fasterxml.jackson.databind.node.TextNode;
import io.swagger.models.Xml;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.BooleanValueProperty;
import io.swagger.models.properties.ComposedProperty;
import io.swagger.models.properties.MapProperty;
import io.swagger.models.properties.ObjectProperty;
Expand Down Expand Up @@ -201,6 +202,10 @@ private Map<PropertyBuilder.PropertyId, Object> argsFromNode(JsonNode node) {
}

Property propertyFromNode(JsonNode node) {

if (node.isBoolean()) {
return new BooleanValueProperty(node.asBoolean());
}
final String type = getString(node, PropertyBuilder.PropertyId.TYPE);
final String title = getString(node, PropertyBuilder.PropertyId.TITLE);
final String format = getString(node, PropertyBuilder.PropertyId.FORMAT);
Expand Down Expand Up @@ -237,6 +242,18 @@ Property propertyFromNode(JsonNode node) {
mapProperty.setReadOnly(readOnly);
return mapProperty;
}
} else if (detailNode != null && detailNode.getNodeType().equals(JsonNodeType.BOOLEAN)) {
Property items = new BooleanValueProperty(detailNode.asBoolean());
MapProperty mapProperty = new MapProperty(items)
.description(description)
.title(title)
.xml(xml);
mapProperty.setExample(example);
mapProperty.setMinProperties(getInteger(node, PropertyBuilder.PropertyId.MIN_PROPERTIES));
mapProperty.setMaxProperties(getInteger(node, PropertyBuilder.PropertyId.MAX_PROPERTIES));
mapProperty.setVendorExtensionMap(getVendorExtensions(node));
mapProperty.setReadOnly(readOnly);
return mapProperty;
} else {
JsonNode allOfNode = node.get("allOf");
detailNode = node.get("properties");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.swagger;

import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.matchers.SerializationMatchers;
import io.swagger.models.ModelImpl;
import io.swagger.models.Swagger;
import io.swagger.util.Yaml;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;

public class BooleanModelTest {
@Test
public void testBooleanProperty() throws JsonProcessingException {
String yaml = "swagger: '2.0'\n" +
"info:\n" +
" title: Some API\n" +
" description: >-\n" +
" This is the Some Api\n" +
" version: 2.0.0\n" +
"basePath: /somepath\n" +
"schemes:\n" +
" - https\n" +
"consumes:\n" +
" - application/json\n" +
"produces:\n" +
" - application/json\n" +
"paths:\n" +
" /somepath:\n" +
" get:\n" +
" description: >\n" +
" my description\n" +
" operationId: MyGet\n" +
" responses:\n" +
" '200':\n" +
" $ref: '#/responses/Response'\n" +
"responses:\n" +
" Response:\n" +
" description: Response\n" +
" schema:\n" +
" type: object\n" +
" required:\n" +
" - Report\n" +
" properties:\n" +
" Report:\n" +
" type: string\n" +
" additionalProperties: false";

Swagger swagger = Yaml.mapper().readValue(yaml, Swagger.class);
assertEquals(((ModelImpl)swagger.getResponses().get("Response").getResponseSchema()).getAdditionalProperties().getBooleanValue().booleanValue(), false);
SerializationMatchers.assertEqualsToYaml(swagger, "swagger: \"2.0\"\n" +
"info:\n" +
" description: \"This is the Some Api\"\n" +
" version: \"2.0.0\"\n" +
" title: \"Some API\"\n" +
"basePath: \"/somepath\"\n" +
"schemes:\n" +
"- \"https\"\n" +
"consumes:\n" +
"- \"application/json\"\n" +
"produces:\n" +
"- \"application/json\"\n" +
"paths:\n" +
" /somepath:\n" +
" get:\n" +
" description: \"my description\\n\"\n" +
" operationId: \"MyGet\"\n" +
" parameters: []\n" +
" responses:\n" +
" \"200\":\n" +
" $ref: \"#/responses/Response\"\n" +
"responses:\n" +
" Response:\n" +
" description: \"Response\"\n" +
" schema:\n" +
" type: \"object\"\n" +
" required:\n" +
" - \"Report\"\n" +
" properties:\n" +
" Report:\n" +
" type: \"string\"\n" +
" additionalProperties: false");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ public abstract class AbstractModel implements Model {
protected Map<String, Property> properties;
protected List<String> required;

protected Boolean booleanValue;

@JsonIgnore
public Boolean getBooleanValue() {
return booleanValue;
}

public void setBooleanValue(Boolean booleanValue) {
this.booleanValue = booleanValue;
}

@Override
public ExternalDocs getExternalDocs() {
return externalDocs;
Expand Down Expand Up @@ -128,7 +139,7 @@ public void setPattern(String pattern) {
public void setVendorExtensions(Map<String, Object> vendorExtensions) {
this.vendorExtensions = vendorExtensions;
}

public Map<String, Property> getProperties() {
return properties;
}
Expand All @@ -140,7 +151,7 @@ public void setProperties(Map<String, Property> properties) {
}
}
}

public void addProperty(String key, Property property) {
if (property == null) {
return;
Expand All @@ -157,7 +168,7 @@ public void addProperty(String key, Property property) {
}
properties.put(key, property);
}

public List<String> getRequired() {
List<String> output = new ArrayList<String>();
if (properties != null) {
Expand Down Expand Up @@ -216,6 +227,7 @@ public void cloneTo(Object clone) {
} else {
cloned.xml = (Xml) xml.clone();
}
cloned.booleanValue = booleanValue;
}

public Object clone() {
Expand All @@ -242,6 +254,7 @@ public int hashCode() {
result = prime * result + (pattern != null ? pattern.hashCode() : 0);
result = prime * result + (properties != null ? properties.hashCode() : 0);
result = prime * result + (required != null ? required.hashCode() : 0);
result = prime * result + (booleanValue != null ? booleanValue.hashCode() : 0);
return result;
}

Expand Down Expand Up @@ -315,15 +328,20 @@ public boolean equals(Object obj) {
}
if (pattern != null ? !pattern.equals(other.pattern) : other.pattern != null) {
return false;
}
}
if (required != null ? !required.equals(other.required) : other.required != null) {
return false;
}
if (properties != null ? !properties.equals(other.properties) : other.properties != null) {
return false;
}

if (booleanValue != null ? !booleanValue.equals(other.booleanValue) : other.booleanValue != null) {
return false;
}

return maximum != null ? maximum.equals(other.maximum) : other.maximum == null;

}

@JsonIgnore
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.swagger.models;

public class BooleanValueModel extends AbstractModel {

public BooleanValueModel() {}
public BooleanValueModel(Boolean booleanValue) {
this.setBooleanValue(booleanValue);
}
@Override
public String getDescription() {
return null;
}

@Override
public void setDescription(String description) {

}

@Override
public Object getExample() {
return null;
}

@Override
public void setExample(Object example) {

}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.swagger.models;

import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.models.properties.Property;

import java.util.Map;
Expand Down Expand Up @@ -30,4 +31,9 @@ public interface Model {
Object clone();

Map<String, Object> getVendorExtensions();

@JsonIgnore
Boolean getBooleanValue();

void setBooleanValue(Boolean booleanValue);
}
Loading