Как создать пример POJO из аннотаций Swagger ApiModelProperty? - PullRequest
0 голосов
/ 11 июня 2018

Мы создаем REST API, который задокументирован с использованием аннотаций @ApiModelProperty Swagger.Я пишу сквозные тесты для API, и мне нужно сгенерировать тело JSON для некоторых запросов.Предположим, мне нужно отправить следующий JSON в конечную точку:

{ "name": "dan", "age": "33" }

До сих пор я создал отдельный класс, содержащий все необходимые свойства и который можно сериализовать в JSON с помощью Jackson:

@JsonIgnoreProperties(ignoreUnknown = true)
public class MyPostRequest {
  private String name;
  private String age;
  // getters and fluid setters omitted...
  public static MyPostRequest getExample() {
    return new MyPostRequest().setName("dan").setAge("33");
  }
}

Однако мы заметили, что у нас уже есть очень похожий класс в кодовой базе, который определяет модель, которую принимает API.В этом классе модели значения примеров для каждого свойства уже определены в @ApiModelProperty:

@ApiModel(value = "MyAPIModel")
public class MyAPIModel extends AbstractModel {

  @ApiModelProperty(required = true, example = "dan")
  private String name;

  @ApiModelProperty(required = true, example = "33")
  private String age;

}

Существует ли простой способ создания экземпляра MyAPIModel, заполненного примерами значений для каждого свойства?Примечание. Мне нужно иметь возможность изменять отдельные свойства в моем сквозном тесте перед преобразованием в JSON, чтобы протестировать различные крайние случаи.Поэтому недостаточно генерировать пример JSON напрямую.

По сути, я могу написать статический метод getExample () для MyAPIModel (или даже лучше для базового класса AbstractModel), который возвращает примерный экземпляр MyAPIModel, как указанов аннотациях Swagger?

1 Ответ

0 голосов
/ 18 июня 2018

Это не представляется возможным на момент этого ответа.Ближайшие возможности, которые я нашел:

  1. io.swagger.converter.ModelConverters: метод read() создает Model объектов, но член example в этих моделях равен нулю.Примеры присутствуют в элементе properties в строковой форме (взятом непосредственно из аннотаций APIModelParameter).

  2. io.swagger.codegen.examples.ExampleGenerator: метод resolveModelToExample() получает выходные данные из ModelConverters.read()и генерирует карту, представляющую объект с его свойствами (в то же время анализируя не строковые свойства, такие как вложенные модели).Этот метод используется для сериализации в JSON.К сожалению, resolveModelToExample() является приватным.Если бы он был общедоступным, код для создания модели по умолчанию для аннотированного класса модели Swagger API мог бы выглядеть следующим образом:

protected <T extends AbstractModel> T getModelExample(Class<T> clazz) {
    // Get the swagger model instance including properties list with examples
    Map<String,Model> models = ModelConverters.getInstance().read(clazz);
    // Parse non-string example values into proper objects, and compile a map of properties representing an example object
    ExampleGenerator eg = new ExampleGenerator(models);
    Object resolved = eg.resolveModelToExample(clazz.getSimpleName(), null, new HashSet<String>());
    if (!(resolved instanceof Map<?,?>)) {
        // Model is not an instance of io.swagger.models.ModelImpl, and therefore no example can be resolved
        return null;
    }
    T result = clazz.newInstance();
    BeanUtils.populate(result, (Map<?,?>) resolved);
    return result;
}
Поскольку в нашем случае нам нужны только свойства String, boolean и int, есть, по крайней мере, возможность самостоятельно анализировать аннотации сумасшедшим хакерским способом:
protected <T extends MyModelBaseClass> T getModelExample(Class<T> clazz) {
    try {
        T result = clazz.newInstance();
        for(Field field  : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(ApiModelProperty.class)) {
                String exampleValue = field.getAnnotation(ApiModelProperty.class).example();
                if (exampleValue != null) {
                    boolean accessible = field.isAccessible();
                    field.setAccessible(true);
                    setField(result, field, exampleValue);
                    field.setAccessible(accessible);
                }
            }
        }
        return result;
    } catch (InstantiationException | IllegalAccessException e) {
        throw new IllegalArgumentException("Could not create model example", e);
    }
}

private <T extends MyModelBaseClass> void setField(T model, Field field, String value) throws IllegalArgumentException, IllegalAccessException {
    Class<?> type = field.getType();
    LOGGER.info(type.toString());
    if (String.class.equals(type)) {
        field.set(model, value);
    } else if (Boolean.TYPE.equals(type) || Boolean.class.equals(type)) {
        field.set(model, Boolean.parseBoolean(value));
    } else if (Integer.TYPE.equals(type) || Integer.class.equals(type)) {
        field.set(model, Integer.parseInt(value));
    }
}

Я мог бы открытьпроблема / PR на Github позже, чтобы предложить добавить функциональность в Swagger.Я очень удивлен, что никто другой, похоже, не запрашивал эту функцию, учитывая, что наш сценарий использования отправки образцов экземпляров модели в API в качестве теста должен быть обычным явлением.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...