0

I am trying to add new attribut in swagger definition in my java spring project. I have read documentation and specially https://springfox.github.io/springfox/docs/snapshot/#plugins

But i was not able to add new attribute from scratch

I am trying this code but it's not really what i want:

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Predicates;
import fr.hop.springdatarest.demo.entity.City;
import lombok.extern.java.Log;
import springfox.documentation.builders.ModelPropertyBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.Model;
import springfox.documentation.schema.ModelProperty;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

@Log
@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1008)
public class SwaggerDefinitionAddin implements ModelBuilderPlugin {

    @Autowired
    TypeResolver resolver;

    Map<String, ModelProperty> cityAddinMap = new HashMap<String, ModelProperty>();

    @Override
    public boolean supports(DocumentationType delimiter) {
        return DocumentationType.SWAGGER_2.equals(delimiter);
    }

    private Class<?> forClass(ModelContext context) {
        return resolver.resolve(context.getType()).getErasedType();
    }

    @Override
    public void apply(ModelContext modelContext) {

        if(forClass(modelContext) == City.class){

            modelContext.getBuilder().id("TEST").properties(????).build();
        }       
    }

}

For exemple i have this definition:

"definitions": {
    "City": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "format": "int64"
        },
        "name": {
          "type": "string"
        },
        "postalCode": {
          "type": "integer",
          "format": "int32"
        }
      }
    },

And i want:

"definitions": {
    "City": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "format": "int64"
        },
        "name": {
          "type": "string"
        },
        "postalCode": {
          "type": "integer",
          "format": "int32"
        },
        "meteo": {
          "$ref": "#/definitions/Meteo"
        }
      }
    },

Can you help me to add the meteo attribut in the definition ? In this case my goal is adding attribut programmatically without using annotation.

Mr2dishes
  • 407
  • 1
  • 6
  • 17

1 Answers1

2

After some research and code test i found this working solution :

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1008)
public class SwaggerDefinitionAddin implements ModelBuilderPlugin {

    @Autowired
    TypeResolver resolver;

    @Autowired 
    TypeNameExtractor typeNameExtractor;

    Map<String, ModelProperty> propertyAddinMap = new HashMap<String, ModelProperty>();



    @Override
    public boolean supports(DocumentationType delimiter) {
        return DocumentationType.SWAGGER_2.equals(delimiter);
    }

    private Class<?> forClass(ModelContext context) {
        return resolver.resolve(context.getType()).getErasedType();
    }

    @Override
    public void apply(ModelContext modelContext) {

        boolean cityScope = false;
        Class<?> modelClass = forClass(modelContext);

        // Detect if it is City modelcontext type instance
        if(modelClass == City.class) {
            cityScope = true;
        }
        // Or an Hateoas resource pointing on City instance
        else if(modelClass.equals(Resource.class)){
            ResolvedType resourceResolveType = resolver.resolve(modelContext.getType()).getTypeBindings().getTypeParameters().get(0);
            if(resourceResolveType.getErasedType().equals(City.class))
                cityScope = true;
        }

        // Add meteo definition in City definition and ResourceCity definition
        if(cityScope){

            ModelPropertyBuilder builder = new ModelPropertyBuilder();
            ModelProperty meteoProperty = builder               
                .name("meteo")
                .type(resolver.resolve(Meteo.class))
                .required(true)
                .isHidden(false)                
                .position(0)                
                .build();

            meteoProperty.updateModelRef(modelRefFactory(modelContext, typeNameExtractor));
            propertyAddinMap.put("meteo",meteoProperty);

            modelContext.getBuilder()           
                .name("City")               
                .properties(propertyAddinMap)
                .build();               
        }       
    }
}

I had some difficulties to found why springfox generate NPE and after lookat source code i found that we have to set the modelRef through:

meteoProperty.updateModelRef(modelRefFactory(modelContext, typeNameExtractor));

Hope that help someone else :)

Mr2dishes
  • 407
  • 1
  • 6
  • 17
  • 1
    I tried your code, but I did not find modelRefFactory method in your example. – Jean-Marc Astesana Oct 02 '18 at 11:45
  • Thanks for your answer. With springfox 3.0.0 now modelRefFactory requires 3 params, as following `modelRefFactory​(ModelContext parentContext, EnumTypeDeterminer enumTypeDeterminer, TypeNameExtractor typeNameExtractor)` https://springfox.github.io/springfox/javadoc/current/springfox/documentation/schema/ResolvedTypes.html Kindly can you guide what can I use for enumTypeDeterminer. – iTech Apr 27 '22 at 12:48