Извлечение сложных данных

Извлечение сложных данных #

Васильев Андрей Михайлович, 2024

Версии презентации


Добавление собственных сообщений об ошибках #

Линзы в http4k предоставляют большой набор методов для преобразования строковых значений в стандартные типы: числа, непустые строки, UUID, даты и т.д.

В рамках приложения бывает недостаточно просто преобразовать данные, нужно дополнять дополнительными ограничениями:

  • Нужно только положительное целое число
  • Идентификатор должен соответствовать реально существующему элементу

Для решения такой задачи спецификации линз можно уточнить с помощью функции map

diagram


Варианты функции map #

Двусторонняя спецификация линзы предоставляет 3 варианта данной функции

Первый вариант функции map принимает в качестве аргумента 2 функции:

  • Функция для преобразования из входного типа в выходной
  • Функция для преобразования из выходного во входной тип

Результат вызова данной функции — двусторонняя линза

Второй вариант функции map принимает в качестве аргумента 1 функцию:

  • Функцию для преобразования из входного типа в выходной

Результат вызова данной функции — односторонняя линза

Третий вариант функции map принимает в качестве аргумента объект типа BiDiMapping


Создание двусторонней линзы для положительных целых чисел #

  • Воспользуемся первой формой функции map
  • В случае проблем с данными будем выбрасывать исключение LensFailure
val positiveIntLens = Query.int().map(
    { number ->
        if (number <= 1) {
            throw LensFailure(
                message = "Параметр number должен быть положительным"
            )
        }
        number
    },
    { number -> number },
).defaulted("number", 1)
  • Первая функция принимает целое число и добавляет проверку данных
  • Вторая функция обеспечивает обратное преобразование целого числа в целое число
  • Название поле указывается дважды: в терминаторе и сообщении об ошибке
  • В качестве альтернативы можно выбросить IllegalArgumentException, но тогда сообщение будет стандартизировано
  • Линзу можно использовать как для чтения, так и для записи

Введение собственных типов для линз #

Накладывание дополнительных ограничений на ввод де-факто создаёт новый тип данных, который может использоваться несколько раз

Для создания подобного рода линз есть два пути:

  • Создать собственную базовую спецификацию линзы
  • Вынести логику по преобразованию данных в объект BiDiMapping
val positiveIntMapping = BiDiMapping(
    { number: Int ->
        if (number <= 1) {
            throw IllegalArgumentException("Поле должно быть положительным")
        }
        number
    },
    { number: Int -> number },
)
val positiveIntLens = Query.int().map(positiveIntMapping)
    .defaulted("number", 1)

Внутри BiDiMapping невозможно создать сообщение с указанием названия поля


Создание объединённых параметров запроса #

Зачастую параметры, которые приходят от пользователя не являются независимыми и их было бы удобно обрабатывать как единую логическую единицу

Примером такого набора данных могут служить параметры постраничного вывода

Для объединения нескольких линз можно воспользоваться функцией composite

data class Pageable(val sortAscending: Boolean, val page: Int,
    val maxResults: Int)
val pageableLens = Query.composite { request ->
    Pageable(
        boolean().defaulted("sortAscending", true)(request),
        int().defaulted("page", 1)(request),
        int().defaulted("maxResults", 20)(request)
    )
}
val pageable: Pageable = pageableLens(request)
  • Можно объединять только линзы, работающие с одним источником: путём запроса, параметры запроса, заголовки и т.д.
  • Только первая ошибка будет выведена пользователю

© A. M. Васильев, 2024, CC BY-SA 4.0, andrey@crafted.su