2. Описание подключения к ПОДД СМЭВ

2.1. Подключение участников взаимодействия с использованием ПОДД СМЭВ

Основным способом направления обращений является использование Личного кабинета СЦ https://sc.minsvyaz.ru.

Электронная почта sd@sc.minsvyaz.ru является резервным способом направления обращений, который используется в случае недоступности Личного кабинета СЦ.

Более подробная информация о порядке подключения к СМЭВ 4 приведена в документе «Регламент подключения к СМЭВ 4» [18].

2.2. Протокол взаимодействия Агента ПОДД СМЭВ и Витрины Поставщика данных

Агент ПОДД СМЭВ Поставщика данных может взаимодействовать с несколькими Витринами данных. Протокол коммуникации Агента и Витрин реализован в виде обмена сообщениями с использованием зарезервированных топиков брокера сообщений Apache Kafka.

2.2.1. Перечень топиков брокера сообщений Apache Kafka

Конфигурация Агента содержит перечень Витрин данных, с которыми он взаимодействует. Каждой Витрине в зависимости от настроек конфигурации, заданных в соответствии с «Руководством администратора ПОДД СМЭВ»[19] соответствует один из наборов топиков:

  1. Набор топиков, создаваемый по умолчанию. Полное название топиков формируются по шаблону <префикс для динамически регистрируемых Витрин>.<название топика>. По умолчанию префикс отсутствует.

  2. Дополнительный набор топиков. Полное название топиков формируются по следующему шаблону <префикс для статически регистрируемых Витрин>.<название топика>. По умолчанию создаются отдельные группы топиков для каждой схемы Витрины с префиксом, соответствующим мнемонике Витрины.

Названия топиков брокера сообщений [20] приведены в Таблица 2.3.

Таблица 2.3 Названия топиков брокера сообщений Apache Kafka

Топик

Публикатор

Подписчик

Передаваемый объект

Топики регистрации и настройки Витрин

1

datamart.signal

Витрина

Агент

Сообщение с регистрационными данными Витрины

2

<префикс>.profile.rq

Агент

Витрина

Запрос профиля Витрины

3

<префикс>.profile.rs

Витрина

Агент

Профиль Витрины

4

<префикс>.profile.err

Витрина

Агент

Ошибка при выполнении запроса профиля Витрины

5

<префикс>.signal

Агент

Витрина

Сообщение с итогами выполнения регистрации Витрины

Топики для обеспечения информационного обмена с использованием SQL-запросов

6

<префикс>.query.rq

Агент

Витрина

Подзапрос

7

<префикс>.procedure.query.rq

Агент

Витрина

Регламентированный запрос на исполнение

8

<префикс>.query.tp

Агент

Витрина

Подзапрос с использованием табличного параметра

9

<префикс>.query.tp.bin

Агент

Витрина

Подзапрос с использованием табличного параметра (при бинарном разбиении на чанки)

10

<префикс>.query.rs

Витрина

Агент

Результата подзапроса

11

<префикс>.query.estimation.rs

Витрина

Агент

Оценка (статистика) по исполнению подзапросов

12

<префикс>.query.err

Витрина

Агент

Ошибка при выполнении подзапроса

13

<префикс>.blob.rq

Агент

Витрина

Запрос двоичных данных по ссылке

14

<префикс>.blob.rs

Витрина

Агент

Результат запроса двоичных данных по ссылке

15

<префикс>.blob.err

Витрина

Агент

Ошибка при выполнении запроса двоичных данных по ссылке

16

<префикс>.cancel.rq

Агент

Витрина

Идентификатора запроса, выполнение которого в Витрине нужно отменить

17

<префикс>.cancel.rs

Витрина

Агент

Результат успешной отмены запроса

18

<префикс>.cancel.err

Витрина

Агент

Ошибка при выполнении отмены запроса

Топики для обеспечения информационного обмена с использованием Рассылок

19

<префикс>.replication.rq

Агент

Витрина

Информация о подписке

20

<префикс>.replication.rs

Витрина

Агент

Структура данных для хранения данных по подписке

21

<префикс>.replication.err

Витрина

Агент

Ошибка при обработке подписки

22

<префикс>.delta.rq

Агент

Витрина

Запрос пакета дельт по подписке

23

<префикс>.delta.tp

Агент

Витрина

Запрос на пересечение ключей и дельт по пересечённым ключам

24

<префикс>.delta.rs

Витрина

Агент

Дельта по подписке

25

<префикс>.delta.err

Витрина

Агент

Ошибка при формировании Витриной дельты по подписке

26

<префикс>.replication.cancel.rq

Агент

Витрина

Идентификатора отменяемой подписки

27

<префикс>.replication.cancel.rs

Витрина

Агент

Результат (успешный или ошибка) отмены подписки

28

<префикс>.delta.notification

Витрина

Агент

Уведомление о наличии дельты по подписке в Витрине Поставщика

Топики для получения статистики по Витринам

29

<префикс>.statistic.rq

Агент

Витрина

Запрос статистики таблиц

30

<префикс>.statistic.rs

Витрина

Агент

Статистика таблиц

31

<префикс>.statistic.err

Витрина

Агент

Ошибка формировании статистики таблиц

Топики для получения событий Витрины

32

<префикс>.scl.signal

Витрина

Агент

События Витрины для дальнейшей передачи в СЦЛ

Топики для временного хранения сообщений при недоступности ПОДД

33

<идентификатор ядра ПОДД>. <мнемоника агента>.undelivered. message

Агент

Агент

Сообщения от Витрины для передачи в ПОДД

2.2.2. Настройка Агента ПОДД для работы с несколькими Витринами данных

В ПОДД предусмотрено два способа регистрации Витрин данных:

  • на основе сообщений от Витрин;

  • на основе конфигурационного файла.

Регистрация Витрины осуществляется вне зависимости от наличия загруженных в ПОДД метаданных Витрин (Раздел 1.5.2). Выполнение запросов к Витринам возможно только после успешной регистрации Витрины и получения метаданных в ПОДД СМЭВ.

2.2.2.1. Регистрация на основе конфигурационного файла

Поддерживается на стороне Потребителя и Поставщика. Таблица 2.4 содержит описание процесса регистрации Витрины.

Таблица 2.4 Процесс регистрации Витрины данных

Процесс

Шаг

Описание

Инициация регистрации

1

При запуске Агент вычитывает локальный конфигурационный файл [21] с настройками регистрации Витрин. Витрина может быть зарегистрирована:

  • статически (при явном указании в конфигурационном файле)

  • динамически (автоматическая регистрация Витрины при обращению к/от Витрины, без необходимости указания в конфигурационном файле)

и создает согласно настройкам в конфигурационном файле:

  • группу топиков по умолчанию (для динамически регистрируемых Витрин)

  • одну или несколько дополнительных групп топиков (для статически регистрируемых Витрин)

2

Если одному из Агентов приходит сообщение из Ядра для незарегистрированной Витрины, Агент регистрирует её динамически (и при необходимости создает группу топиков по умолчанию).

3

Если инициатором сообщения будет Витрина или ИС Потребителя, они будут зарегистрированы динамически, если при старте Агента создана группа топиков по умолчанию.

Регистрация

4

Агент загружает профиль Витрины. Способ загрузки определяется значением registrationFlow в конфигурационном файле:

  • DEFINED_PROFILE – загрузка по указанному в конфигурации пути (по умолчанию);

  • DATAMART_REQUEST – запрос к Витрине через топик <префикс>.profile.rq

5

Витрина данных передаёт:

  • либо профиль Витрины через топик <префикс>.profile.rs

  • либо сообщение об ошибке в топик <префикс>.profile.err

6

Агент, получивший запрос от Витрины:

  • уведомляет Ядро ПОДД СМЭВ о регистрации профиля новой

Витрины,

  • передаёт в Ядро ПОДД СМЭВ профиль Витрины

  • ожидает подтверждения регистрации от Ядра ПОДД

7

Ядро ПОДД: осуществляет регистрацию

8

Направляет в Агент подтверждение регистрации Витрины.

При успешном выполнении всех регистрационных операций, либо если данная Витрина уже зарегистрирована возвращается статус «успех», в противном случае «не успех».

Действия после успешной Регистрации

9

Агент (при запуске) вычитывает метаданные Витрин из Ядра ПОДД через специальный топик.

Чтение метаданных не препятствует запуску, то есть в процессе чтения Агент уже может принимать запросы.

Обновление метаданных производится при каждом перезапуске Агента

Действия после не успешной Регистрации

10

Агент завершает работу с ошибкой

2.2.2.2. Регистрация на основе сообщений Витрин

Только для Поставщика.

Таблица 2.5 содержит описание процесса регистрации Витрины, схема регистрации на основе сообщения от Витрины приведена на Рисунок - 2.1.

Таблица 2.5 Процесс регистрации Витрины данных

Процесс

Шаг

Описание

Инициация регистрации

1

Витрина данных направляет в Агент сообщение с регистрационными данными в общий топик Kafka datamart.signal предназначенный для получения запросов на регистрацию от всех Витрин. Структура сообщения приведена в Приложение 2 Структуры сообщений для взаимодействия с Поставщиком

При отправке запроса на регистрацию Витрина должна обеспечить подписку на топик signal

Регистрация

2

Агент создаёт необходимые для работы с новой Витриной данных топики в соответствии с Таблица 2.3.

3

Агент запрашивает у Витрины профиль через топик profile.rq.

4

Агент получает:

  • либо профиль Витрины через топик profile.rs

  • либо сообщение об ошибке в топик profile.err

5

Агент:

  • уведомляет Ядро ПОДД СМЭВ о регистрации профиля новой Витрины,

  • передаёт в Ядро ПОДД СМЭВ профиль Витрины,

  • ожидает подтверждения регистрации от Ядра ПОДД.

6

Агент получает подтверждение регистрации профиля Витрины от Ядра ПОДД СМЭВ.

При успешном выполнении всех регистрационных операций, либо если данная Витрина уже зарегистрирована возвращается статус «успех», в противном случае «не успех».

7

Агент направляет Витрине сообщение о статусе регистрации.

8

В случае неуспешной регистрации Агент удаляет созданные на шаге 2 топики в Kafka.

Действия после успешной Регистрации

9

Агент выполняет запрос к Ядру ПОДД СМЭВ за актуальной информацией о зарегистрированных Витринах (метаданные Витрины и иные данные, необходимые для корректной работы) [22] и обновляет по полученным данным хранимую локально сводную информацию.

Агент завершает процедуру запуска только после получения актуальных данных.

Действия после неуспешной Регистрации

10

В случае ошибок, таймаутов и сбоев, инициация повторной регистрации обеспечивается Витриной.

Процесс регистрации Витрины данных на основе сообщения от Витрины данных

Рисунок - 2.1 Процесс регистрации Витрины данных на основе сообщения от Витрины данных

2.2.3. Примеры реализации взаимодействия с Агентом ПОДД СМЭВ с использованием брокера сообщений Apache Kafka

Далее приведены примеры программного кода, реализующие следующие процедуры с использованием брокера сообщений Apache Kafka:

  • получение запроса от Агента ПОДД СМЭВ;

  • передача результата Агенту ПОДД СМЭВ.

Пример проекта для реализации взаимодействия с Агентом приведен в Приложение 1 Пример проекта для реализации взаимодействия с Агентом ПОДД.

2.2.3.1. Получение запроса от Агента ПОДД СМЭВ

Пример программного кода для получения запроса от Агента ПОДД СМЭВ с использованием брокера сообщений Apache Kafka:

package ru.rtlabs.sample

import mu.KLogging
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.apache.kafka.clients.consumer.KafkaConsumer
import org.apache.kafka.clients.producer.KafkaProducer
import org.apache.kafka.clients.producer.Producer
import ru.rtlabs.sample.messaging.MessageHandler
import ru.rtlabs.sample.query.QueryRequestMessageHandler
import ru.rtlabs.sample.tableParams.TableParamStorageStub
import java.time.Duration
import java.util.Properties

/**
* Пример конфигурации обработчиков сообщений от агента
*/
suspend fun main() {

    val kafkaProperties = loadProperties("/kafka.properties")
    val producer = KafkaProducer<ByteArray, ByteArray>(kafkaProperties)

    val topicProperties = loadProperties("/topic.properties")
    val handlers = mapOf<String, MessageHandler>(
        queryRequestHandler(topicProperties, producer),
    )

    KafkaConsumer<ByteArray, ByteArray>(kafkaProperties).apply {
        subscribe(handlers.keys)
    }.use {
        while (true) {
            it.poll(Duration.ofMillis(1_000)).forEach { record ->
                val topic = record.topic()
                val handler = handlers[topic] ?: HandlerNotFound(topic)
                handler.handle(record)
            }
        }
    }
}

fun loadProperties(fileName: String) = Properties().apply {
    this::class.java.getResourceAsStream(fileName).use {
        load(it)
    }
}

fun queryRequestHandler(topicProperties: Properties, producer: Producer<ByteArray, ByteArray>) = Pair(
    topicProperties.getProperty("query.requestTopicName"),
    QueryRequestMessageHandler(
        producer = producer,
        errorTopic = topicProperties.getProperty("query.errorTopicName"),
        resultTopic = topicProperties.getProperty("query.resultTopicName"),
        estimationTopic = topicProperties.getProperty("query.estimationTopicName"),
        tableParamStorage = TableParamStorageStub(),
    ),
)

private class HandlerNotFound(val topic: String) : MessageHandler {
    override suspend fun handle(message: ConsumerRecord<ByteArray, ByteArray>) {
        logger.error { "Не сконфигурирован обработчик для топика $topic" }
    }

    companion object : KLogging()
}

2.2.3.2. Передача результата Агенту ПОДД СМЭВ

Пример программного кода для передачи результата выполнения запрос Агенту ПОДД СМЭВ с использованием брокера сообщений Apache Kafka:

package ru.rtlabs.sample.query

import org.apache.kafka.clients.producer.Producer
import org.apache.kafka.clients.producer.ProducerRecord
import ru.rtlabs.common.model.DatamartErrorType
import ru.rtlabs.common.model.KeyValueMessage
import ru.rtlabs.common.model.metadata.ColumnInfo
import ru.rtlabs.common.model.metadata.ColumnType
import ru.rtlabs.common.result.resultEncoder
import ru.rtlabs.common.serialization.AvroCodec
import ru.rtlabs.common.serialization.UUIDCodec
import ru.rtlabs.contract.datamart.query.*
import ru.rtlabs.contract.datamart.query.blob.BinaryReference
import ru.rtlabs.sample.messaging.RequestChunkedResultHandler
import ru.rtlabs.sample.messaging.putHeaders
import ru.rtlabs.sample.tableParams.TableParamStorage
import java.io.ByteArrayOutputStream
import java.math.BigDecimal
import java.nio.ByteBuffer
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.UUID

/**
* Реализация обработчика входящих сообщений на выполнение
* sql запросов к базе
*/
class QueryRequestMessageHandler(
    producer: Producer<ByteArray, ByteArray>,
    errorTopic: String,
    resultTopic: String,
    private val estimationTopic: String,
    private val tableParamStorage: TableParamStorage,
) : RequestChunkedResultHandler<
    UUID,
    DatamartExecuteQueryRequest,
    DatamartExecuteQueryResultChunk,
    DatamartExecuteQueryError,
>(
    producer = producer,
    requestKeyDecoder = UUIDCodec::decode,
    requestDecoder = AvroCodec(DatamartExecuteQueryRequest.serializer()).decodeFunction(),
    resultKeyEncoder = AvroCodec(DatamartExecuteQueryResultChunk.serializer()).encodeFunction(),
    resultTopic = resultTopic,
    errorKeyEncoder = { UUIDCodec.encode(it.subRequestId) },
    errorEncoder = AvroCodec(DatamartExecuteQueryError.serializer()).encodeFunction(),
    errorTopic = errorTopic
) {
    override val logger = logger()

    private val estimationCodec = AvroCodec(DatamartQueryEstimationResponse.serializer())

    override suspend fun process(request: KeyValueMessage<UUID, DatamartExecuteQueryRequest>) {
        val executeRequest = request.value
        val initialSql = executeRequest.sql
        logger.debug { "Входящий sql запрос [$initialSql]" }
        val tableParams = executeRequest.tableParams
        if (tableParams.isEmpty()) {
            if (executeRequest.isForEstimation) {
                respondEstimation(request)
            } else {
                respond(initialSql, request)
            }
            return
        }

        try {
            waitUntilAllParametersLoaded(tableParams, executeRequest.subRequestId)
            val resultSql = tableParamStorage.replaceTableParams(initialSql, tableParams)
            logger.debug { "Sql запрос к исполнению с использованием табличных параметров [$resultSql]" }
            respond(resultSql, request)
        } finally {
            deleteAllParameters(tableParams, executeRequest.subRequestId)
        }
    }

    private fun respondEstimation(request: KeyValueMessage<UUID, DatamartExecuteQueryRequest>) {
        val executeRequest = request.value
        val estimation = DatamartQueryEstimationResponse(
            requestId = executeRequest.requestId,
            subRequestId = executeRequest.subRequestId,
            estimatedRowCount = 100,
            estimatedSize = 10_000,
            estimatedTime = 117,
        )

        producer.send(
            ProducerRecord(
                estimationTopic,
                UUIDCodec.encode(executeRequest.subRequestId),
                estimationCodec.encode(estimation),
            ).putHeaders(request.headers),
        )
    }

    private fun respond(sql: String, request: KeyValueMessage<UUID, DatamartExecuteQueryRequest>) {
        logger.debug { "Выполняется sql запрос [$sql]" }
        val streamTotal = 2
        val chunkTotal = 3
        val executeRequest = request.value
        for (stream in 1..streamTotal) {
            for (chunk in 1..chunkTotal) {
                val resultValue = prepareResult(executeRequest.subRequestId)
                val isLastChunk = chunk == chunkTotal
                val resultKey = prepareResultKey(
                    request = executeRequest,
                    chunkNumber = chunk,
                    isLastChunk = isLastChunk,
                    streamNumber = stream,
                    streamTotal = streamTotal,
                    uncompressedSize = resultValue.size,
                )
                sendResult(KeyValueMessage(resultKey, resultValue, request.headers))
            }
        }
    }

    private suspend fun waitUntilAllParametersLoaded(tableParams: List<DatamartTableParam>, subRequestId: UUID) {
        tableParams.forEach {
            tableParamStorage.wait(subRequestId, it)
        }
    }

    private suspend fun deleteAllParameters(tableParams: List<DatamartTableParam>, subRequestId: UUID) {
        tableParams.forEach {
            tableParamStorage.delete(subRequestId, it)
        }
    }

    private fun prepareResultKey(
        request: DatamartExecuteQueryRequest,
        chunkNumber: Int,
        isLastChunk: Boolean,
        streamNumber: Int,
        streamTotal: Int,
        uncompressedSize: Int,
    ) = DatamartExecuteQueryResultChunk(
        requestId = request.requestId,
        subRequestId = request.subRequestId,
        replyTo = request.replyTo,
        chunkNumber = chunkNumber,
        isLastChunk = isLastChunk,
        streamNumber = streamNumber,
        streamTotal = streamTotal,
        isFragmented = true,
        uncompressedSize = uncompressedSize,
    )

    private fun prepareResult(request: DatamartExecuteQueryRequest): ByteArray {
        val metadata = listOf(
            ColumnInfo("string_col", ColumnType.STRING),
            ColumnInfo("long_col", ColumnType.LONG),
            ColumnInfo("int_col", ColumnType.INTEGER),
            ColumnInfo("big_decimal_col", ColumnType.BIG_DECIMAL),
            ColumnInfo("double_col", ColumnType.DOUBLE),
            ColumnInfo("float_col", ColumnType.FLOAT),
            ColumnInfo("date_col", ColumnType.DATE),
            ColumnInfo("timestamp_col", ColumnType.TIMESTAMP),
            ColumnInfo("bool_col", ColumnType.BOOLEAN),
            ColumnInfo("binary_col", ColumnType.BINARY)
        )

        val buf = ByteBuffer.wrap(ByteArray(4) { it.toByte() })
        val reference = BinaryReference(
            subRequestId = request.subRequestId,
            path = "Высокое дерево на плече Подзорной Трубы, направление к С. от С.-С.-В.",
        )

        @Suppress("RemoveRedundantCallsOfConversionMethods")
        val rows: List<Array<Any?>> = listOf(
            arrayOf(
                "s1",
                1L,
                1.toInt(),
                BigDecimal.TEN.setScale(3) / BigDecimal("3"),
                1.2,
                1.2f,
                LocalDate.parse("2007-12-03"),
                LocalDateTime.parse("2007-12-03T10:15:30").plusNanos(123456),
                true,
                buf
            ),
            arrayOf(
                "s2",
                2L,
                2.toInt(),
                BigDecimal.TEN.setScale(3) / BigDecimal("6"),
                2.2,
                2.2f,
                LocalDate.parse("2007-12-03"),
                LocalDateTime.parse("2007-12-03T10:15:30").minusNanos(654321),
                true,
                reference
            ),
            arrayOf(null, null, null, null, null, null, null, null, null, null)
        )

        return ByteArrayOutputStream().apply {
            resultEncoder(metadata, this).use { encoder ->
                rows.forEach(encoder::append)
            }
        }.toByteArray()
    }

    override fun createError(
        request: KeyValueMessage<UUID, DatamartExecuteQueryRequest>,
        e: Exception,
        type: DatamartErrorType?,
    ) = DatamartExecuteQueryError(
        requestId = request.value.requestId,
        subRequestId = request.value.subRequestId,
        replyTo = request.value.replyTo,
        errorCode = (type ?: DatamartErrorType.QUERY).code,
        message = e.message ?: ""
    )

}

2.3. Протокол взаимодействия Агента ПОДД СМЭВ и ИС Потребителя данных

Для обеспечения доступности данных Агент ПОДД СМЭВ предоставляет:

  • REST-интерфейс для выполнения запросов к Витринам Поставщиков данных (Раздел 2.3.1 данного документа);

  • REST-интерфейс для выполнения запросов к REST-сервису ИС Ответчика (Раздел 2.3.2 документа).

  • специализированный протокол для исполнения запросов к Витринам Поставщиков данных с использованием JDBC-интерфейса Агента ПОДД (Раздел 2.3.3 данного документа).

2.3.1. REST-интерфейс Агента ПОДД для SQL-запросов

В Агенте ПОДД СМЭВ реализована поддержка REST-интерфейса для выполнения запросов к Витринам Поставщиков данных.

URL-адрес для выполнения обращений к REST-интерфейсу имеет следующий формат: http://<адрес>:<порт>/query, где:

  • <адрес> – IP-адрес Агента ПОДД СМЭВ;

  • <порт> – порт, на котором развернут REST-интерфейс в соответствии с «Руководством администратора ПОДД СМЭВ» [23].

Входные параметры, включая текст SQL-запроса (в случае обращения к Витринам Поставщиков данных), должны кодироваться в виде JSON-строки и передаваться в теле запроса.

Результат выполнения SQL-запроса передается в теле HTTP-ответа в виде JSON-строки. Файлы, передаваемые в составе результата выполнения SQL-запроса, включаются в JSON как строковые атрибуты, кодирующие содержимое передаваемого файла с использованием Base64.

Файлы в составе результата выполнения запроса BLOB по ссылке передаются в виде массива байт. Возможно выполнение запросов к Витринам Поставщиков данных:

2.3.1.1. Выполнение SQL-запросов (синхронный режим)

В синхронном режиме получение результата осуществляется путем выполнения HTTP-запроса от ИС Потребителя к Агенту ПОДД.

В рамках HTTP-запроса (метод POST) передается SQL-запрос, в ответе возвращается результат выполнения SQL-запроса.

2.3.1.1.1. HTTP-запрос
Таблица 2.6 HTTP-запрос

Параметр

Тип

Обязательность

Описание

1

Тип запроса (Method)

Да

POST

2

Путь (Path)

Да

<IP:port>/query

header

1

Content-Type

string

Да

application/x-www-form-urlencoded; charset=utf-8

2

Accept-version

string

Да

Основная (major) часть версии (сейчас 1)

3

ClientRequestID

string

Нет

Клиентский идентификатор

query

1

async

boolean

Нет

Для синхронного режима выполнения запроса параметр должен отсутствовать или иметь значение False

body

1

priority

string

Да

Приоритет запроса. Варианты:

  • NORMAL;

  • HIGH

2

timeout

string

Нет

Предельное время ожидания выполнения запроса.

Запросы без указания данного параметра использовать не рекомендуется, следует указать значение, равное времени ожидания ответа на ИС Потребителя. Максимальное значение 24 часа

В случае отсутствия параметра в запросе таймаут

имеет значение по умолчанию 1 час

3

sql

string

Да

Текст SQL-запроса к Витринам Поставщиков данных

4

params

array

Нет

Параметры запроса

4.1

type

string

Да

Тип параметра

4.2

value

string

Да

Значение параметра

5

maxRows

string

Нет

Максимальное количество возвращаемых записей таблицы ответа. Если не задан, возвращаются все записи.

Пример запроса:

POST «https://10.81.4.30:29354/query?async=false»

Accept-Version:1
Content-Type:application/x-www-form-urlencoded; encoding=utf-8

priority:NORMAL
timeout:60
sql:SELECT ao.oktmo, o.name, o.kod from fias.addrobj ao LEFT JOIN oktmo.oktmo o on ao.oktmo = o.kod2 WHERE ao.offname= ? AND o.regionid = ?
params:{ “type”: “STRING”, “value”:”Москва”},{ “type”: “INTEGER”, “value”: “18”}
2.3.1.1.2. HTTP-запрос на вызов Регламентированного SQL-запроса без надстроек
Таблица 2.7 HTTP-запрос на вызов Регламентированного SQL-запроса без надстроек

Параметр

Тип

Обязательность

Описание

1

Тип запроса (Method)

Да

POST

2

Путь (Path)

Да

<IP:port>/regulated-query

Заголовки

1

Content-Type

string

Да

application/x-www-form-urlencoded; charset=utf-8

2

Accept-version

string

Да

Основная (major) часть версии (сейчас 1)

3

ClientRequestID

string

Нет

Клиентский идентификатор

Тело сообщения

1

priority

string

Да

Приоритет запроса. Варианты:

  • NORMAL;

  • HIGH

2

timeout

string

Нет

Предельное время ожидания выполнения запроса.

Запросы без указания данного параметра использовать не рекомендуется, следует указать значение, равное времени ожидания ответа на ИС Потребителя. Максимальное значение 24 часа

В случае отсутствия параметра в запросе таймаут

имеет значение по умолчанию 1 час

3

datamart

string

Нет

Витрина Поставщика данных, к которой производится обращение

4

mnemonic

string

Нет

Мнемоника РЗ, к которому производится обращение

5

majorVersion

int

Нет

Если не указана majorVersion и minorVersion, обращение к актуальной версии

Версия РЗ, к которому производится обращение

6

majorVersion

int

Нет

Если не указана majorVersion и minorVersion, обращение к актуальной версии

Если не указана minorVersion и указанана majorVersion, обращение к последней версии в рамках majorVersion

Версия РЗ, к которому производится обращение

7

params

array

Нет

Параметры запроса

7.1

type

string

Да

Тип параметра

7.2

value

string

Да

Значение параметра

Пример запроса:

POST «https://10.81.4.30:29354/regulated-query»
Accept-Version:1
Content-Type:application/x-www-form-urlencoded; encoding=utf-8

priority:NORMAL
timeout:60
datamart: fias
mnemonic: addrobj_view
majorVersion: 1
minorVersion: 0
2.3.1.1.3. Ответ на HTTP-запрос

Допустимые коды возврата:

  • 200 – ок;

  • 400 – ошибка в запросе, информация об ошибке содержится в параметре «error»;

  • 403 – нет полномочий на выполнение запроса, в том числе при блокировке полномочий на стороне Поставщика (с отображением соответствующего текста ошибки);

  • 406 – неподдерживаемая версия протокола (после внедрения поддержки обратной совместимости возвращается только для несуществующих версий);

  • 429 – ИС УВ временно заблокирована в связи с превышением лимитов;

  • 500 – системная ошибка (в связи в принятыми ограничениями по доступности код 500 предполагается только при сбое Агента);

  • 503 – система временно недоступна, возможно повторить запрос через 50 мс.

Таблица 2.8 Параметры ответа с кодом возврата 200

Параметр

Тип

Обязательность

Описание

1

responseCode

numeric

Да

Код возврата (HTTP-код)

header

1

Content-Type

string

Да

application/vnd.ru.rtlabs.podd.agent+json; version=1.0; charset=utf-8

2

ClientRequestID

string

Нет

Клиентский идентификатор

body

1

created_at

dateTime

Да

Время формирования ответа

2

query_id

string

Да

Идентификатор запроса

3

rows

array

Нет

Массив записей таблицы ответа (в случае если результат выполнения запроса в виде таблицы).

При задании параметра «maxRows» ограничивается его значением

3.1

array

Да

Массив значений.

Возможным значением может быть содержимое файла, закодированное в формате BASE64

4

meta

array

Да

Список полей результата

4.1

name

string

Да

Имя поля

4.2

type

string

Да

Тип поля

Пример ответа с кодом возврата 200:

HTTP/1.1 200 OK
{
“created_at”: “2017-12-15T07:36:-03Z”,
“query_id”: “c005a0e7-0d26-4ce0-a1fa-10c8bdf4dfc5”,
    “meta”: [
    {
        “name”: “count”,
        “type”: “INTEGER”
    }
],
«rows»: [
        [
            «4994»
        ]
]
}

Пример ответа при возврате BLOB и ссылки на BLOB:

HTTP/1.1 200 OK
{
    "created_at": "2023-02-21T14:15:46Z",
    "query_id": "1edb1f23-90c1-6b75-bd1a-914f21e14802",
    "meta": [
        {
            "name": "id",
            "type": "INTEGER"
        },
        {
            "name": "name",
            "type": "STRING"
        },
        {
            "name": "logo_thumb",
            "type": "BINARY"
        }
    ],
    "rows": [
При возврате BLOB:
        [
            "2",
            "scala",
            "R0lGODlhIAAPAPUAADcNCTgNCW0ZEW8aEnMbEnYcE3wdFH0dFIAeFIEeFIMfFYUfFZAiF5IiF5MiF5okGKEmGaYnGqsoG7YrHbgrHb0sHsUuH8cvH8gvINExIdIxIdcyItozItszItszI940I980I+A0I+A1I+E1I+E1JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUAIf8LSW1hZ2VNYWdpY2sNZ2FtbWE9MC40NTQ1NQAsAAAAACAADwAABoDAknBILBqPSOInyTR+ntBlc1r6gECi0WcjDASoyI0GQ4EoCt3SN7k+MggCwiJySQ+9X7y66yV6MhocICQjfHlGendEISOEHxkVRImGfWqVQhYTEQ0GAwclAJR8lnmHQw8ICQ4SFx2Gr3h6sUMhVyMjIFJgSVFPu02+v8LDxMUlQQA7"
        ],
При возврате ссылки на BLOB:
        [
            "17",
            "test",
            "link://c005a0e7-0d26-4ce0-a1fa-10c8bdf4dfc5"
        ]
    ]

}
Таблица 2.9 Параметры ответа с кодом возврата, отличным от 200

Параметр

Тип

Обязательность

Описание

1

responseCode

numeric

Да

Код возврата (HTTP-код)

header

1

Content-Type

string

Да

application/vnd.ru.rtlabs.podd.agent+json; version=1.0; charset=utf-8

2

ClientRequestID

string

Нет

Клиентский идентификатор

body

1

created_at

dateTime

Да

Время формирования ответа

2

query_id

string

Да

Идентификатор запроса

3

error

string

Нет

Текст ошибки

Пример ответа с кодом возврата, отличным от 200:

HTTP/1.1 429 Too Many Requests

{
    “created_at”: “2021-09-10T15:23:36Z”,
    “query_id”: “1ec124b1-1aa6-66d6-9d40-f55438428f56”,
    “error”: “RuntimeException: Ошибка во входящем потоке : CustomRSocketException: LIMIT_EXCEEDED: Запросы к Ядру ПОДД временно заблокированы до September 10, 2021 3:24:35 PM UTC, код причины блокировки=2, подробности: ‘Превышен лимит по количеству запросов lockId=fe462d49-3c5a-4350-8bf6-da155225c52f, userId=e92e3fd4-28d9-48e5-8079-e377b676c9b4 reqCountLimit=10, QueriesStatistic(totalSent=10, totalBytes=2870, totalRows=0, requestIds={1ec124b0-bce0-613b-9d40-c7c6585b14bc=287B, 1ec124b0-caa5-677c-9d40-ebd5d2c0409b=287B, 1ec124b0-d464-695d-9d40-bbafa86207b7=287B, 1ec124b0-dcd5-63ae-9d40-171562fc3a47=287B, 1ec124b0-e493-6a6f-9d40-174dab9e6065=287B, 1ec124b0-ec15-60a0-9d40-f300de6b4e34=287B, 1ec124b0-f6de-6451-9d40-51926f0b5d81=287B, 1ec124b0-fe95-65e2-9d40-e5e27f87babb=287B, 1ec124b1-0581-6d43-9d40-6301af351da7=287B, 1ec124b1-0c9c-6ad4-9d40-dfc04d23e0bc=287B})’.”
}

2.3.1.2. Выполнение SQL-запросов (асинхронный режим)

В асинхронном режиме получение результата осуществляется путем выполнения двух HTTP-запросов от ИС Потребителя к Агенту ПОДД СМЭВ:

  1. В рамках первого HTTP-запроса (метод POST) передается SQL-запрос, в ответе возвращается идентификатор запроса.

  2. В рамках второго HTTP-запроса (метод GET) передается ранее полученный идентификатор запроса, в ответе возвращается результат выполнения SQL-запроса. Получение результата по указанному идентификатору возможно только один раз.

2.3.1.2.1. HTTP-запрос передачи SQL-запроса в Агент ПОДД (метод POST)
2.3.1.2.1.1. HTTP-запрос на вызов запроса с надстройками
Таблица 2.10 HTTP-запрос на вызов запроса с надстройками

Параметр

Тип

Обязательность

Описание

1

Тип запроса (Method)

Да

POST

2

Путь (Path)

Да

<IP:port>/query

header

1

Content-Type

string

Да

application/x-www-form-urlencoded; charset=utf-8

2

Accept-version

string

Да

Основная (major) часть версии (сейчас 1)

3

ClientRequestID

string

Нет

Клиентский идентификатор

query

1

async

boolean

Нет

Для асинхронного режима выполнения запроса параметр должен отсутствовать или иметь значение True

body

1

priority

string

Да

Приоритет запроса. Варианты:

  • NORMAL;

  • HIGH

2

timeout

string

Нет

Предельное время ожидания выполнения запроса.

Запросы без указания данного параметра использовать не рекомендуется, следует указать значение, равное времени ожидания ответа на ИС Потребителя. Максимальное значение 24 часа

В случае отсутствия параметра в запросе таймаут

имеет значение по умолчанию 1 час

3

sql

string

Да

Текст SQL-запроса

4

params

array

Нет

Параметры запроса

4.1

type

string

Да

Тип параметра

4.2

value

string

Да

Значение параметра

5

maxRows

string

Нет

Максимальное количество возвращаемых записей таблицы ответа. Если не задан, возвращаются все записи.

Пример запроса:

POST «https://10.81.4.30:29354/query?async=true»

Accept-Version:1
Content-Type:application/x-www-form-urlencoded; encoding=utf-8

priority:NORMAL
timeout:60
sql:SELECT ao.oktmo, o.name, o.kod from fias.addrobj ao LEFT JOIN oktmo.oktmo o on ao.oktmo = o.kod2 WHERE ao.offname= ? AND o.regionid = ?
params:{ “type”: “STRING”, “value”:”Москва”},{ “type”: “INTEGER”, “value”: “18”}
2.3.1.2.1.2. HTTP-запрос на вызов запроса без надстроек
Таблица 2.11 HTTP-запрос на вызов запроса без надстроек

Параметр

Тип

Обязательность

Описание

1

Тип запроса (Method)

Да

POST

2

Путь (Path)

Да

<IP:port>/regulated-query/async

Заголовки

1

Content-Type

string

Да

application/x-www-form-urlencoded; charset=utf-8

2

Accept-version

string

Да

Основная (major) часть версии (сейчас 1)

3

ClientRequestID

string

Нет

Клиентский идентификатор

Тело сообщения

1

priority

string

Да

Приоритет запроса. Варианты:

  • NORMAL;

  • HIGH

2

timeout

string

Нет

Предельное время ожидания выполнения запроса.

Запросы без указания данного параметра использовать не рекомендуется, следует указать значение, равное времени ожидания ответа на ИС Потребителя. Максимальное значение 24 часа

В случае отсутствия параметра в запросе таймаут

имеет значение по умолчанию 1 час

3

datamart

string

Нет

Витрина Поставщика данных, к которой производится обращение

4

mnemonic

string

Нет

Мнемоника РЗ, к которому производится обращение

5

majorVersion

int

Нет

Если не указана majorVersion и minorVersion, обращение к актуальной версии

Версия РЗ, к которому производится обращение

6

majorVersion

int

Нет

Если не указана majorVersion и minorVersion, обращение к актуальной версии

Если не указана minorVersion и указанана majorVersion, обращение к последней версии в рамках majorVersion

Версия РЗ, к которому производится обращение

7

params

array

Нет

Параметры запроса

7.1

type

string

Да

Тип параметра

7.2

value

string

Да

Значение параметра

Пример запроса:

POST «https://10.81.4.30:29354/regulated-query/async»
Accept-Version:1
Content-Type:application/x-www-form-urlencoded; encoding=utf-8

priority:NORMAL
timeout:60
datamart: fias
mnemonic: addrobj_view
majorVersion: 1
minorVersion: 0
params:{ “type”: “STRING”, “value”:”Москва”},{ “type”: “INTEGER”, “value”: “18”}
2.3.1.2.1.3. Ответ на HTTP-запрос

Допустимые коды возврата:

  • 201 – запрос создан;

  • 400 – ошибка в запросе, информация об ошибке содержится в параметре «error»;

  • 403 – нет полномочий на выполнение запроса, в том числе при блокировке полномочий на стороне Поставщика (с отображением соответствующего текста ошибки);

  • 429 – ИС УВ временно заблокирована в связи с превышением лимитов;

  • 503 – система временно недоступна, возможно повторить запрос через 50 мс.

Таблица 2.12 Параметры ответа с кодом возврата 201:

Параметр

Тип

Обязательность

Описание

1

responseCode

numeric

Да

Код возврата (HTTP-код)

header

1

Content-Type

string

Да

application/vnd.ru.rtlabs.podd.agent+json; version=1.0; charset=utf-8

2

Location

string

Да

Временная ссылка на скачивание результата выполнения запроса

3

ClientRequestID

string

Нет

Клиентский идентификатор

body

1

id

string

Да

Уникальный идентификатор SQL-запроса

2

deadline

string

Да

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

Пример ответа с кодом возврата 201:

HTTP/1.1 201 Created
{
    “id”: “a2f05175-d5bc-47d4-9b88-17930630683e”,
    «deadline»: «2021-05-13T06:33:43Z»
}
Таблица 2.13 Параметры ответа с кодом возврата 400:

Параметр

Тип

Обязательность

Описание

responseCode

numeric

Да

Код возврата (HTTP-код)

header

Content-Type

string

Да

application/vnd.ru.rtlabs.podd.agent+json; version=1.0; charset=utf-8

body

error

string

Нет

Текст ошибки

2.3.1.2.2. HTTP-запрос получения результата, ранее переданного в Агент ПОДД СМЭВ асинхронного SQL-запроса (метод GET)
2.3.1.2.2.1. HTTP-запрос при вызове исходного запроса с надстройками
Таблица 2.14 HTTP-запрос при вызове исходного запроса с надстройками

Параметр

Тип

Обязательность

Описание

Тип запроса (Method)

Да

GET

Путь (Path)

Да

<IP:port>/query

header

Accept

string

Да

application/vnd.ru.rtlabs.podd.agent+json; charset=utf-8

Accept-version

string

Да

Основная (major) часть версии (сейчас 1)

ClientRequestID | string | Нет | Клиентский идентификатор

query

query_id

string

Да

Уникальный идентификатор SQL-запроса

Пример запроса:

GET “https://10.81.4.30:29354/query/c005a0e7-0d26-4ce0-a1fa-10c8bdf4dfc5”
2.3.1.2.2.2. HTTP-запрос при вызове исходного запроса без надстроек
Таблица 2.15 HTTP-запрос при вызове исходного запроса без надстроек

Параметр

Тип

Обязательность

Описание

1

Тип запроса (Method)

Да

GET

2

Путь (Path)

Да

<IP:port>/regulated-query

Параметры

1

query_id

string

Да

Уникальный идентификатор SQL-запроса

Заголовки

1

Accept

string

Да

application/vnd.ru.rtlabs.podd.agent+json; charset=utf-8

2

Accept-version

string

Да

Основная (major) часть версии (сейчас 1)

3

ClientRequestID

string

Нет

Клиентский идентификатор

Пример запроса:

GET “https://10.81.4.30:29354/ regulated-query/c005a0e7-0d26-4ce0-a1fa-10c8bdf4dfc5”
2.3.1.2.2.3. Ответ на HTTP-запрос

Допустимые коды возврата:

  • 503 – система временно недоступна, возможно повторить запрос через 50 мс;

  • 500 – системная ошибка (в связи в принятыми ограничениями по доступности код 500 предполагается только при сбое Агента);

  • 429 – ИС УВ временно заблокирована в связи с превышением лимитов;

  • 406 – неподдерживаемая версия протокола (после внедрения поддержки обратной совместимости возвращается только для несуществующих версий);

  • 404 – Результат по заданному идентификатору SQL-запроса не найден;

  • 403 – нет полномочий на выполнение запроса, в том числе при блокировке полномочий на стороне Поставщика (с отображением соответствующего текста ошибки);

  • 400 – ошибка в запросе, информация об ошибке содержится в параметре «error»;

  • 202 – результат по заданному идентификатору SQL-запроса еще не поступил;

  • 200 – ок.

Таблица 2.16 Параметры ответа с кодом возврата 200:

Параметр

Тип

Обязательность

Описание

1

responseCode

numeric

Да

Код возврата (HTTP-код)

header

1

Content-Type

string

Да

application/vnd.ru.rtlabs.podd.agent+json; version=1.0; charset=utf-8

2

ClientRequestID

string

Нет

Клиентский идентификатор

body

1

created_at

dateTime

Да

Время формирования ответа. Время, с которого ответ доступен для получения по запросу

2

query_id

string

Да

Идентификатор запроса

3

rows

array

Нет

Массив записей таблицы ответа (в случае если результат выполнения запроса в виде таблицы).

При задании параметра «maxRows» ограничивается его значением

3.1

array

Да

Массив значений.

Возможным значением может быть содержимое файла, закодированное в формате BASE64

4

meta

array

Да

Список полей результата

4.1

name

string

Да

Имя поля

4.2

type

string

Да

Тип поля

Пример ответа с кодом возврата 200:

HTTP/1.1 200 OK
{
“created_at”: “2017-12-15T07:36:03Z”,
“query_id”: “c005a0e7-0d26-4ce0-a1fa-10c8bdf4dfc5”,
    “meta”: [
    {
        “name”: “count”,
        “type”: “INTEGER”
    }
],
«rows»: [
        [
            «4994»
        ]
]
}
Таблица 2.17 Параметры ответа с кодом возврата, отличным от 200:

Параметр

Тип

Обязательность

Описание

1

responseCode

numeric

Да

Код возврата (HTTP-код)

header

1

Content-Type

string

Да

application/vnd.ru.rtlabs.podd.agent+json; version=1.0; charset=utf-8

2

ClientRequestID

string

Нет

Клиентский идентификатор

body

1

query_id

string

Нет

Идентификатор запроса

2

created_at

dateTime

Нет

Время формирования ответа – время, с которого ответ доступен для получения по запросу

3

error

string

Нет

Текст ошибки

2.3.1.3. Выполнение запроса с табличным параметром

Может быть выполнен как в синхронном, так и в асинхронном режиме. Ответ соответствует способу вызова.

2.3.1.3.1. HTTP-запрос с табличным параметром на вызов запроса с надстройками
Таблица 2.18 HTTP-запрос с табличным параметром на вызов запроса с надстройками

Параметр

Тип

Обязательность

Описание

1

Тип запроса (Method)

Да

POST

2

Путь (Path)

Да

<IP:port>/query

query

1

async

boolean

Нет

Для синхронного режима выполнения запроса параметр должен отсутствовать или иметь значение False

Для асинхронного режима выполнения запроса параметр должен иметь значение True

header

1

Content-Type

string

Да

multipart/form-data;

2

Connection

string

Да

keep-alive

3

Keep-Alive:

string

Да

300

4

ClientRequestID

string

Нет

Клиентский идентификатор

boundary

1

Content-Disposition

application/json name=request

2

Content-Type

string

Да

application/json; charset=utf-8

3

Accept-version

string

Да

Основная (major) часть версии (сейчас 1)

json

1

priority

string

Да

Приоритет запроса. Варианты:

  • NORMAL;

  • HIGH

2

timeout

string

Нет

Предельное время ожидания выполнения запроса в секундах

В случае отсутствия параметра в запросе таймаут не выставляется

3

sql

string

Да

Текст SQL-запроса

4

tableParams

array

Да

Описание передаваемого файла с данными для табличного параметра

4.1

name

string

Да

Наименование табличного параметра, соответствующее указанному в SQL-выражении в формате @<name>

4.2

columns

array

Да

Перечень наименований столбцов и их типов, содержащихся в файле с данными для табличного параметра

4.2.1

name

string

Да

Наименование столбца

4.2.2

type

string

Да

Тип столбца

5

<name>

Да

Файл с данными для табличного параметра. Имя параметра соответствует наименованию табличного параметра

file

5.1

файл в формате CSV

Да

Файл в формате CSV (поддерживаемый формат), передаваемый в параметре запроса

boundary

1

Content-Disposition

string

Да

form-data; name=”table1”; filename=”table1.csv”

Пример запроса приведен в Раздел 3.3.4.1 настоящего документа.

2.3.1.3.2. HTTP-запрос с табличным параметром на вызов запроса без надстроек
Таблица 2.19 HTTP-запрос с табличным параметром на вызов запроса без надстроек

Параметр

Тип

Обязательность

Описание

1

Тип запроса (Method)

Да

POST

2

Путь (Path)

Да

<IP:port>/regulated-query – для синхронного вызова

<IP:port>/regulated-query/async – для асинхронного вызова

Заголовки

1

Content-Type

string

Да

multipart/form-data;

2

Connection

string

Да

keep-alive

3

Accept-version

string

Да

Основная (major) часть версии (сейчас 1)

4

ClientRequestID

string

Нет

Клиентский идентификатор

Тело запроса

1

priority

string

Да

Приоритет запроса. Варианты:

  • NORMAL;

  • HIGH

2

timeout

string

Нет

Предельное время ожидания выполнения запроса.

В случае отсутствия параметра в запросе таймаут

не выставляется

3

datamart

string

Нет

Витрина Поставщика данных, к которой производится обращение

4

mnemonic

string

Да

Мнемоника РЗ, к которому производится обращение

5

majorVersion

int

Нет

Обязательное указание вместе с minorVersion. Если не указана, обращение к актуальной версии

Версия РЗ, к которому производится обращение

6

majorVersion

int

Нет

Обязательное указание вместе с majorVersion. Если не указана, обращение к актуальной версии

Версия РЗ, к которому производится обращение

7

tableParams

array

Да

Описание передаваемого файла с данными для табличного параметра

7.1

name

string

Да

Наименование табличного параметра,

соответствующее указанному в SQL-выражении в формате @<name>

7.2

columns

array

Да

Перечень наименований столбцов и их типов, содержащихся в файле с данными для табличного параметра

7.2.1

name

string

Да

Наименование столбца

7.2.2

type

string

Да

Тип столбца

8

<name>

Да

Файл с данными для табличного параметра. Имя параметра соответствует наименованию табличного параметра

file

8.1

файл в формате CSV

Да

Файл в формате CSV (поддерживаемый формат), передаваемый в параметре запроса

boundary

1

Content-Disposition

string

Да

form-data; name=”table1”; filename=”table1.csv”

2.3.1.4. Выполнение запроса на получение BLOB по ссылке

2.3.1.4.1. HTTP-запрос (вариант 1)
Таблица 2.20 HTTP-запрос (вариант 1)

Параметр

Тип

Обязательность

Значение / описание

1

Тип запроса (Method)

Да

GET

2

Путь (Path)

Да

<IP:port>/query/blob

Параметры

1

link

string

Да

Ссылка на вырузку BLOB

Заголовки

1

Accept

string

Да

application/vnd.ru.rtlabs.podd.agent+json; charset=utf-8

2

Accept-version

string

Да

Основная (major) часть версии (сейчас 1)

3

ClientRequestID

string

Нет

Клиентский идентификатор в формате UUID.

Потребителю рекомендуется в запросе BLOB по ссылке указать тот же сквозной идентификатор, что и при исходном запросе.

Ответственность за соответствие идентификаторов лежит на Потребителе.

Пример запроса:

GET “https://10.81.4.30:29354/query/blob/c005a0e7-0d26-4ce0-a1fa-10c8bdf4dfc5”
2.3.1.4.2. HTTP-запрос (вариант 2)
Таблица 2.21 HTTP-запрос (вариант 2)

Параметр

Тип

Обязательность

Значение / описание

1

Тип запроса (Method)

Да

GET

2

Путь (Path)

Да

<IP:port>/regulated-query/blob

Параметры

1

link

string

Да

Ссылка на вырузку BLOB

Заголовки

1

Accept

string

Да

application/vnd.ru.rtlabs.podd.agent+json; charset=utf-8

2

Accept-version

string

Да

Основная (major) часть версии (сейчас 1)

3

ClientRequestID

string

Нет

Клиентский идентификатор в формате UUID.

Потребителю рекомендуется в запросе BLOB по ссылке указать тот же сквозной идентификатор, что и при исходном запросе.

Ответственность за соответствие идентификаторов лежит на Потребителе.

Пример запроса:

GET “https://10.81.4.30:29354/regulated-query/blob/c005a0e7-0d26-4ce0-a1fa-10c8bdf4dfc5”
2.3.1.4.3. Ответ на HTTP-запрос

Допустимые коды возврата:

  • 200 – ок;

  • 400 – ошибка в запросе, информация об ошибке содержится в параметре «error»;

  • 403 – нет полномочий на выполнение запроса, в том числе при блокировке полномочий на стороне Поставщика (с отображением соответствующего текста ошибки);

  • 404 – получен запрос на обращение к недоступной ссылке на BLOB (истекло время жизни);

  • 406 – неподдерживаемая версия протокола (после внедрения поддержки обратной совместимости возвращается только для несуществующих версий);

  • 429 – ИС УВ временно заблокирована в связи с превышением лимитов;

  • 500 – системная ошибка (в связи в принятыми ограничениями по доступности код 500 предполагается только при сбое Агента);

  • 503 – система временно недоступна, возможно повторить запрос через 50 мс.

Параметры ответа с кодом возврата 200:

Таблица 2.22 Параметры ответа с кодом возврата 200:

Параметр

Тип

Обязательность

Значение / описание

1

responseCode

numeric

Да

Код возврата (HTTP-код)

Заголовки

1

Content-Type

string

Да

application/octet-stream

2

Content-Length

Да

Размер тела объекта в байтах

3

ClientRequestID

string

Нет

Клиентский идентификатор

Тело сообщения

1

Да

Содержимое полученного по ссылке файла (в виде массива байт)

Пример ответа с кодом возврата 200:

HTTP/1.1 200 OK
<Содержимое полученного по ссылке файла (в виде массива байт)>
Таблица 2.23 Параметры ответа с кодом возврата, отличным от 200:

Параметр

Тип

Обязательность

Описание

1

responseCode

numeric

Да

Код возврата (HTTP-код)

Заголовки

1

Content-Type

string

Да

application/vnd.ru.rtlabs.podd.agent+json; version=1.0; charset=utf-8

Тело сообщения

1

created_at

dateTime

Нет

Время формирования ответа

2

query_id

string

Нет

Идентификатор запроса

3

error

string

Нет

Текст ошибки

Пример ответа с кодом возврата, отличным от 200:

HTTP/1.1 429 Too Many Requests
{
    “created_at”: “2021-09-10T15:23:36Z”,
    “query_id”: “1ec124b1-1aa6-66d6-9d40-f55438428f56”,
    “error”: “RuntimeException: Ошибка во входящем потоке : CustomRSocketException: LIMIT_EXCEEDED: Запросы к Ядру ПОДД временно заблокированы до September 10, 2021 3:24:35 PM UTC, код причины блокировки=<>, подробности: ‘Превышен лимит по количеству скачиваний по ссылке... ”
}

2.3.2. REST-интерфейс Агента ПОДД для запросов к REST-сервису ИС Ответчика

2.3.2.1. HTTP-запрос

Все запросы выполняются в синхронном режиме в соответствии с загруженной в ПОДД СМЭВ спецификацией OpenApi REST-сервиса ИС Ответчика. Примеры спецификаций OpenAPI REST-сервисов ИС Ответчиков приведены в Раздел 1.5.5.

Получение результата осуществляется путем выполнения HTTP-запроса от ИС Инициатора к Агенту ПОДД Ответчика. При выполнении запроса могут быть использованы все типы HTTP-методов (POST, GET, PUT и DELETE), выполняющих CRUD-операции (Create/Read/Update/Delete), которые используются в спецификациях OpenAPI, описывающих REST-сервисы ИС Ответчиков.

Информация о формировании запроса приведена в Раздел 3.3.6.

Клиентский идентификатор передаётся в опциональном заголовке ClientRequestID (тип string).

2.3.2.2. Ответ на HTTP-запрос

Ответ на запрос включает код возврата (статус выполнения операции) и, в зависимости от запрошенного метода, тело ответа, содержащее запрошенные данные и/или дополнительную информацию о результате выполнения операции, в соответствии с загруженной в ПОДД спецификацией OpenAPI REST-сервиса ИС Ответчика.

Таблица 2.24 содержит коды возврата ПОДД при ошибках выполнения запроса к REST-сервису ИС Ответчика.

Таблица 2.24 Коды возврата ПОДД

Код возврата

Пример причины ошибки

1

500 Internal Server Error

  • ошибка пересылки сообщений между сервисами

  • непредвиденная ошибка

2

401 Unauthorized

Ошибка проверки подписи

3

404 Not found

Спецификация OpenAPI REST-сервиса ИС Ответчика, к которой обращается запрос, не зарегистрирована в ПОДД

4

403 Forbidden

Нет прав на выполнение запроса к зарегистрированной в ПОДД спецификация OpenAPI REST-сервиса ИС Ответчика

5

400 Bad request

Запрос не соответствует зарегистрированной в ПОДД спецификации OpenAPI REST-сервиса ИС Ответчика

6

429 Too many requests

ИС УВ временно заблокирована в связи с превышением лимитов. Может возвращаться при использовании механизма с возможностью отправки большого запроса

7

503 Service unavailable

Oтветчик ограничил очередь запросов в клиенте. Может возвращаться при использовании механизма без возможности отправки большого запроса

2.3.3. JDBC-интерфейс Агента ПОДД СМЭВ для SQL-запросов

Агент ПОДД поддерживает специализированный протокол для исполнения запросов, эталонная реализация которого представлена JDBC-драйвером.

Настройка JDBC-драйвера осуществляется с помощью передачи специализированной адресной строки: protocol://hostname:port/, где:

  • protocol – протокол взаимодействия – значение всегда будет «podd»;

  • hostname – имя сервера или его IP-адрес;

  • port – порт, на котором Агент Потребителя данных предоставляет интерфейс для работы протокола взаимодействия в соответствии с «Руководством администратора ПОДД СМЭВ»[24].

Клиентский идентификатор опционально передаётся в тексте SQL запроса первой строкой – комментарий с атрибутом ClientRequestID (тип string).

-- ClientRequestID: <клиентский идентификатор>
CALL <мнемоника регламентированного SQL-запроса>();

2.3.3.1. Пример использования JDBC-драйвера в «Kotlin»

Для прикладного разработчика работа с JDBC драйвером PODD ничем не отличается от работы с обычным JDBC драйвером.

Особенность только в URL, которым инициализируется драйвер.

package dev.nsud.jdbc

import org.junit.jupiter.api.Test
import java.sql.Connection
import java.sql.DriverManager
import org.junit.jupiter.api.Assertions
import java.sql.SQLException

class Features {

    private getConnectionURI() {
        val host = System.getProperty("agent.host", "localhost")
        val port = System.getProperty("agent.port", "8182")
        return "jdbc:podd://$host:$port"
    }
    @Test
    fun `ожидается успешное соединение с базой данных`() {
        Assertions.assertDoesNotThrow { DriverManager.getConnection(getConnectionURI()) }
    }

    @Test
    fun `ожидается успешное исполнение запроса вида "select 1" `() {
        Assertions.assertDoesNotThrow {
            val con = DriverManager.getConnection(getConnectionURI())
            val statement = con.createStatement()
            statement.queryTimeout = 5 // таймаут на выполнение запроса - 5 секунд
            statement.setMaxRows(100) // ограничение выборки по кол-ву возвращаемых строк
            statement.executeQuery("select 1")
            val resultSet = statement.resultSet
            Assertions.assertEquals(1, resultSet.getInt(0))
        }
    }

    @Test
    fun `ожидается ошибка при исполнении запроса "select 1" `() {
        Assertions.assertDoesNotThrow {
            val con = DriverManager.getConnection(getConnectionURI())
            val statement = con.createStatement()


            try {
                statement.executeQuery("select 1")
            } catch (e: SQLException) {
                // получение кода ошибки
                Assertions.assertEquals(17089, e.errorCode)
                Assertions.assertEquals("PODD-17089: Ошибка при обработке запроса", e.message)
            }
        }
    }

    @Test
    fun `ожидается успешное получение бинарных данных `() {
        Assertions.assertDoesNotThrow {
            val expect = getExpectedBytes()
            val con = DriverManager.getConnection(getConnectionURI())
            val statement = con.createStatement()
            statement.executeQuery("select binaryColumn from datamart.table where id=1")
            val resultSet = statement.resultSet
            Assertions.assertEquals(expect, resultSet.getBlob(0))
        }
    }


    @Test
    fun `ожидается успешное применение табличных параметров `() {
        Assertions.assertDoesNotThrow {
        DriverManager.getConnection(getConnectionURI()).use { connection ->
            connection.prepareStatement("select * from @p1, @p2, oktmo.oktmo o where @p1.a = @p2.b and o.id = @p1.a").use { ps ->
                ps.queryTimeout = 10 // таймаут на выполнение запроса - 10 секунд
                ps.setMaxRows(100) // ограничение выборки по кол-ву возвращаемых строк
                ps as PoddPreparedStatement
                ps.addTableParam(
                    "p1",
                    listOf(
                        ColumnInfo("a", ColumnType.INTEGER),
                        ColumnInfo("av", ColumnType.STRING),
                    ),
                    iterator<Array<Any?>> {
                        yield(arrayOf(1, "1_1"))
                        yield(arrayOf(2, "1_2"))
                        yield(arrayOf(3, "1_3"))
                    },
                )
                ps.addTableParam(
                    "p2",
                    listOf(
                        ColumnInfo("b", ColumnType.INTEGER),
                        ColumnInfo("bv", ColumnType.STRING),
                    ),
                    iterator<Array<Any?>> {
                        yield(arrayOf(1, "2_1"))
                        yield(arrayOf(2, "2_2"))
                        yield(arrayOf(3, "2_3"))
                    },
                )

                ps.executeQuery().use { rs ->
                    (1..rs.metaData.columnCount).forEach {
                        println("${rs.metaData.getColumnName(it)}: ${rs.metaData.getColumnTypeName(it)}")
                    }
                    rs.readFully().forEach { row ->
                        println()
                        row.forEach {
                            print("$it\t")
                        }
                    }
                }
            }
        }
        }
    }

2.3.3.2. Коды возврата

Таблица 2.25 содержит допустимые коды возврата при ошибках выполнения запроса.

Таблица 2.25 Коды возврата при ошибках выполнения запроса

Код возврата

Описание ошибки

1

17001

Внутренняя ошибка

2

17473

Запрос не прошел проверку корректности (соответствие синтаксису)

3

17471

ИС УВ временно заблокирована в связи с превышением лимитов

4

17800

Запрос содержит указание на неподдерживаемую Витрину

5

17472

Нет полномочий на выполнение запроса

6

17510

Запрос отменен Потребителем

7

17520

Запрос отменен по таймауту

8

17404

Выполнение запроса прекращено из-за блокировки полномочий по результатам проверки на стороне Поставщика

9

17405

Выполнение запроса прекращено из-за блокировки полномочий по результатам проверки на стороне Поставщика (заблокировано по умолчанию)

10

17406

Выполнение запроса прекращено из-за блокировки по результату проверки SQL выражения на стороне Поставщика

2.4. Протокол взаимодействия Агента ПОДД СМЭВ и Витрины данных по подписке Потребителя данных

Протокол коммуникации Агента ПОДД СМЭВ и Витрины данных по подписке, расположенных в контуре Потребителя данных, устроен в виде обмена сообщениями с использованием зарезервированных топиков брокера сообщений Apache Kafka.

Всё взаимодействие между Витриной данных по подписке и Агентом ПОДД СМЭВ происходит исключительно с использованием брокера сообщений.

2.4.1. Перечень топиков брокера сообщений Apache Kafka

Таблица 2.26 содержит названия топиков брокера сообщений .

Таблица 2.26 Названия топиков брокера сообщений Apache Kafka

Топик

Публикатор

Подписчик

Передаваемый объект

Топики для обеспечения информационного обмена с использованием Рассылок

1

<префикс>.replication.in.rq

Агент

Витрина

Структура таблиц Витрины Поставщика данных

2

<префикс>.replication.in.rs

Витрина

Агент

Уведомление об успешном создании структуры данных

3

<префикс>.replication.in.err

Витрина

Агент

Уведомление об ошибке при создании структуры данных

4

<префикс>.delta.notification.in

Агент

Витрина

Уведомление о наличии новых дельт у Поставщика

5

<префикс>.command.podd

ИС Потребителя

Агент

Служебный топик для ручного перезапроса дельт

6

<префикс>.delta.in.rq

Агент

Витрина

Запрос на прием дельты

7

<префикс>.delta.in.tp

Агент

Витрина

Запрос на прием дельт по распределённой подписке

8

<префикс>.delta.in.rs

Витрина

Агент

Уведомления об успешном применении дельт из пакета

9

<префикс>.delta.in.err

Витрина

Агент

Уведомление об ошибке при применении дельт из пакета

10

<префикс>.replication.cancel.in.rq

Агент

Витрина

Идентификатора отменяемой подписки

11

<префикс>.replication.cancel.in.rs

Витрина

Агент

Результат (успешный или ошибка) отмены подписки

Топики для получения событий Витрины

12

<префикс>.scl.signal

Витрина

Агент

События Витрины для дальнейшей передачи в СЦЛ

2.5. Протокол взаимодействия Агента ПОДД СМЭВ и ИС Ответчика

Взаимодействие Агента ПОДД СМЭВ и REST-сервиса на стороне ИС Ответчика осуществляется в соответствии со спецификацией OpenAPI, описывающей REST-сервис ИС Ответчика и загруженной в ПОДД СМЭВ.

Для использования запросов к REST-сервису ИС Ответчика через ПОДД СМЭВ необходимо произвести настройки, указанные в Руководстве администратора Агента ПОДД СМЭВ [25].