Pull to refresh

Comments 5

UFO just landed and posted this here

Я правильно понял, что теперь все зависимости на внешние типы (типа MonetaryAmount или CommandResult) фактически инлайнятся в спецификацию? А как тогда генерируются package names для них: если он одинаковый (типа ru.yandex.money.domain), то как решаете конфликты с разными версиями этого класса (например разные несовместимые версии ru.yandex.money.domain.PhoneNumber в API Notifier и API Кошелька)? Если package names разные (например ru.yandex.money.notifier.PhoneNumber и ru.yandex.money.kassa.PhoneNumber), то как решили проблему конвертации одного в другое?

Я правильно понял, что теперь все зависимости на внешние типы (типа MonetaryAmount или CommandResult) фактически инлайнятся в спецификацию ?

Если тебе хочется что бы твой тип не генерировался, а использовался уже имеющийся у тебя в classpath, то тебе достаточно указать в спецификации расширение

x-java-import-mapping: ru.yoomoney.model.domain.phone.PhoneNumber
x-java-dependency: ru.yoomoney.domain:phone

Это скажет генератору не создавай новый файлик с именем PhoneNumber.java а по всем местам использования этой схемы (PhoneNumber) возьми ru.yoomoney.model.domain.phone.PhoneNumber

Вот сгенерированного объекта запроса

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

import ru.yoomoney.domain.phone.PhoneNumber;

import ru.yoomoney.codegen.platform.ApiToStringBuilder;
import ru.yoomoney.codegen.platform.Jsr303Validator;

import javax.annotation.Nonnull;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

@ApiModel(description = "Запрос отправки ОТП на номер")
public class OtpSendRequest {

    @ApiModelProperty(value = "Номер телефона в формате ITU-T E.164", required = true)
    @NotNull
    private final PhoneNumber phone;

    @ApiModelProperty(
            value = "Секретный код, который получит пользователь",
            example = "1234",
            required = true)
    @NotNull
    @Pattern(regexp = "^[0-9]{4}$")
    private final String secret;

    @ApiModelProperty(
            value = "Уникальный ID запроса в рамках приложения прикладного процесса",
            example = "86463266-f8ce-4a61-a3c6-62647028c111",
            required = true)
    @NotNull
    @Size(max = 40)
    private final String requestId;

    @JsonCreator
    private OtpSendRequest(
            @Nonnull @JsonProperty("phone") PhoneNumber phone,
            @Nonnull @JsonProperty("secret") String secret,
            @Nonnull @JsonProperty("requestId") String requestId) {
        this.phone = phone;
        this.secret = secret;
        this.requestId = requestId;
    }

    @Nonnull
    @JsonProperty("phone")
    public PhoneNumber getPhone() {
        return phone;
    }

    @Nonnull
    @JsonProperty("secret")
    public String getSecret() {
        return secret;
    }

    @Nonnull
    @JsonProperty("requestId")
    public String getRequestId() {
        return requestId;
    }

    @Override
    public String toString() {
        return ApiToStringBuilder.toStringHelper(this)
                .add("phone", phone)
                .add("secret", secret)
                .add("requestId", requestId)
                .toString();
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private PhoneNumber phone;
        private String secret;
        private String requestId;

        public Builder withPhone(PhoneNumber phone) {
            this.phone = phone;
            return this;
        }

        public Builder withSecret(String secret) {
            this.secret = secret;
            return this;
        }

        public Builder withRequestId(String requestId) {
            this.requestId = requestId;
            return this;
        }

        public OtpSendRequest build() {
            return Jsr303Validator.validate(new OtpSendRequest(phone, secret, requestId));
        }
    }
}

Класс ru.yoomoney.domain.phone.PhoneNumber находится в ru.yoomoney.domain:phone:x.y.z

разработчик самостоятельно должен добавить зависимость на библиотеку ru.yoomoney.domain:phone:x.y.z в build.gradle

Расширение x-java-dependency - это такая справочная информация которая поможет понять разработчику где взять недостающий класс.

Если же он забыл добавить ru.yoomoney.domain:phone:x.y.z в build.gradle то при компиляции java кода будет ошибка с тем что класс ru.yoomoney.domain.phone.PhoneNumber не найден.

Это что касается шаринга общих типов между спеками.

Если говорить про CommandResult или ApiToStringBuilder, Jsr303Validator из примера выше.

Эти классы не описаны в спецификациях, т.к. не участвуют в транспорте.

Эти классы в отличи от PhoneNumber нужны абсолютно всем т.к. сгенерированный код зависит от них.

Такие классы генератор несет с собой в ресурсах и инлайнит всегда в одно и тоже место ru.yoomoney.codegen.platform.*

Проблем их конфликтов не возникает т.к. в одном приложении подключена одна версия генератора.

Описание контракта в виде человеко-понятных схем и кодогенерация по ним — в моем представлении — это очень похоже на WSDL. Т.е. история вернулась на круги своя, или я упускаю какую-то принципиальную разницу?

В целом вы правы, описать контракт в понятном для человека языке и сгенерировать по нему код - это не новая идея. Черт кроется в деталях. Основное от WSD по моему мнению - это возможность очень точно управлять тем кодом который вы генерируется. Это позволяет вам использовать именно те технологии которые у вас есть в компании. Для маленьких проектов или стартапов все это наверное излишне. Но если вы большая компания с кучей сервисов и штатом разработчиков, такой подход сильно помогает в разработке.

Sign up to leave a comment.