Top.Mail.Ru
Tdg » 1.6 » 3. Запросы из внешних систем » 3.1. Запросы к данным » 3.1.5. Запросы в формате XML (SOAP)
 
3. Запросы из внешних систем / 3.1. Запросы к данным / 3.1.5. Запросы в формате XML (SOAP)
3. Запросы из внешних систем / 3.1. Запросы к данным / 3.1.5. Запросы в формате XML (SOAP)

3.1.5. Запросы в формате XML (SOAP)

3.1.5. Запросы в формате XML (SOAP)

Рассмотрим обработку запросов в формате XML (SOAP) на основе базового примера. Для отправки запросов мы используем простые скрипты на языке Python. Для приёма и обработки запросов нам понадобится кластер TDG, настроенный ранее.

3.1.5.1. Адаптация конфигурации из базового примера

Для повторения примеров из данного раздела нам потребуется внести определённые исправления в конфигурацию системы, загруженную ранее.

Разархивируйте архив с конфигурацией системы в отдельную директорию. Откройте файл config.yml и измените его так, чтобы он содержал следующий текст:

types:
  __file: model.avsc

functions:
  router: {__file: router.lua}

  classifier: {__file: classificator.lua}

  select_user_books: {__file: select_user_books.lua}

  process_user: {__file: process_user.lua}

  process_book: {__file: process_book.lua}

  process_sub: {__file: process_sub.lua}

pipelines:
  router:
    - router
  classifier:
    - classifier
  select_user_books:
    - select_user_books
  process_user:
    - process_user
  process_book:
    - process_book
  process_sub:
    - process_sub

connector:
  input:
    - name: soap
      type: soap
      wsdl: {__file: example.wsdl}
      handlers:
        - function: Connect
          pipeline: router

  routing:
    - key: input_key
      output: to_input_processor

  output:
    - name: to_input_processor
      type: input_processor

input_processor:
  classifiers:
    - name: classifier
      pipeline: classifier

  routing:
    - key: process_user
      pipeline: process_user
    - key: process_book
      pipeline: process_book
    - key: process_sub
      pipeline: process_sub

  storage:
    - key: add_user
      type: User
    - key: add_book
      type: Book
    - key: add_subscription
      type: Subscription

services:
  select_user_books:
    doc: "select_user_books"
    function: select_user_books
    return_type: string
    args:
      user_id: long

Обратите внимание на появившийся раздел input_processor/routing и связанные с ним описания новых конвейеров обработки данных (pipeline) и функций. При этом модель данных не изменилась. В разделе connector появилось описание входа с типом SOAP, функцией Connect (не являющейся функцией TDG, но описываемой в XML/SOAP) и связанным с ней конвейером (уже известным нам по примеру с JSON -– router).

Примечание

В секции wsdl: обязательно указывается файл спецификации веб-сервиса с расширением .wsdl. В нашем примере это example.wsdl. Данный файл должен обязательно быть включен в состав архива с конфигурацией системы. В том случае, если WSDL не используется, данный файл может быть пустым.

Теперь откройте файл classificator.lua и отредактируйте его так, чтобы он имел следующее содержимое:

#!/usr/bin/env tarantool

    local param = ...

    if (param.obj[1].tag == "username" or param.obj[2].tag == "username") then
        param.routing_key = "process_user"
        return param
    end

    if (param.obj[1].tag == "book_name" or param.obj[2].tag == "book_name") then
        param.routing_key = "process_book"
        return param
    end

    if ((param.obj[1].tag == "user_id" and param.obj[2] == "book_id") or (param.obj[2].tag == "user_id" and param.obj[1].tag == "book_id")) then
        param.routing_key = "process_sub"
        return param
    end

    param.routing_key = "unknown_type"
    return param

Создайте файлы process_user.lua, process_book.lua и process_sub.lua. Содержимое этих файлов –- скрипт обработки данных, который должен формировать из получаемого TDG информационного объекта объект, соответствующий описанному в модели данных.

Далее приведён пример содержимого файла process_user.lua. Остальные файлы выполняются по аналогии.

#!/usr/bin/env tarantool

local param = ...

local id = param.obj[1][1]

local username = param.obj[2][1]

local data = {id = tonumber(id), username = username}

local ret = {obj = data, priority = 1, routing_key = 'add_user'}

return ret

Сформированный вышеуказанным скриптом объект должен содержать поля id и username, так как он получит routing_key для добавления объекта типа User в хранилище.

Важно

Для сохранения TDG ожидает объект в формате, представленном выше, где obj включает в себя весь информационный объект в виде перечисленных через запятую пар key = value, где для каждого обязательного поля объекта в модели данных задан одноимённый ключ (key) с непустым значением.

Закончив с подготовкой файлов, упакуйте их в zip-архив и загрузите его согласно инструкции.

3.1.5.2. Описание процесса обработки запроса

Логика обработки поступающего запроса изложена в файле конфигурации config.yml и состоит в следующем:

  • Согласно разделу connector / input файла конфигурации config.yml, SOAP (XML) запросы передаются на обработку конвейеру router, который состоит из функции router.

  • Функция router ссылается на файл router.lua, который упаковывает поступивший объект с ключом routing_key равным строке input_key.

  • Согласно разделу connector / routing файла конфигурации config.yml, все объекты с ключом input_key передаются to_input_processor.

  • В секции output для раздела connector указана единственная запись to_input_processor, которая переадресует запрос в раздел input_processor для обработки на одноименной роли.

  • В разделе input_processor все запросы попадают в секцию classifiers, где в нашем случае указан один единственный объект, вызывающий конвейер обработки объектов (pipeline) classifier.

  • Конвейер classifier вызывает одноименную функцию, которая описана в файле classificator.lua. Как можно понять из названия, данная функция занимается классификацией поступающей информации. Логика ее работы следующая:

    • при наличии тэга username у поступившего объекта – ему присваивается routing_key = process_user, то есть объект направляется для формирования объекта типа пользователь;
    • при наличии тэга book_name у поступившего объекта – ему присваивается routing_key = process_book, то есть объект направляется для формирования объекта типа книга;
    • при наличии тэгов user_id и book_id у поступившего объекта – ему присваивается routing_key = process_sub, то есть объект направляется для формирования объекта типа подписка;
    • во всех остальных случаях объекту присваивается routing_key = unknown_type, то есть объект нераспознан. Такой объект обычно попадает в ремонтную очередь, но можно настроить и иное поведение.
  • В разделе input_processor в секции routing имеются указания по обработке объектов со следующими routing_key: process_user, process_book и process_sub при помощи одноименных функций. Каждая из этих функций формирует объект в нужном для сохранения формате и присваивает ему соответствующий routing_key.

  • В секции storage описано сохранение данных в TDG.

    • при значении routing_key равном add_user объект сохраняется как User.
    • при значении routing_key равном add_book объект сохраняется как Book.
    • при значении routing_key равном add_subscription объект сохраняется как Subscription.

Обратите внимание, что вся лишняя информация, не относящаяся к типу объекта, описанному в модели данных, не будет сохранена.

Из всего файла конфигурации системы остался не рассмотренным участок, отвечающий за сервисы. В данном примере там описан простой сервис, вызывающий функцию select_user_books с аргументом user_id и возвращающий строковую переменную.

Логика работы этой функции такова - переданное значение user_id используется для поиска в объекте Subscription всех книг, записанных за данным пользователем. Затем выводятся все найденные book_id.

3.1.5.3. Подготовка запроса в формате SOAP (XML)

Создайте файл request.py со скриптом на языке Python для отправки простейшего запроса с полями объекта типа User (из нашей модели данных: это поля id и username), который будет выглядеть следующим образом:

import requests

data = """<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <NS2:Connect xmlns:NS2="http://172.19.0.2:8080/soap">
      <id>1</id>
      <username>John Smith</username>
    </NS2:Connect>
  </soap:Body>
</soap:Envelope>"""

header = {'auth-token' : 'c0b26a60-aebe-4899-9ab6-4458627ac61e'}

r = requests.post(url = "http://172.19.0.2:8080/soap", data = data, headers = header)

Передаваемый запрос содержит заголовок для авторизации и тело в виде SOAP объекта с обязательными полями для объекта модели данных типа User.

Примечание

Используйте в качестве значения для ключа auth-token токен приложений, сгенерированный ранее.

Для успешного добавления записи имеющегося в модели данных типа объекта в запросе должны содержаться все обязательные поля для данного типа объекта.

Процедура отправки запроса и ожидаемые результаты описаны ранее в пункте Отправка запросов.