Статьи

TaxCloud: интеграция сервисов SOAP в Ruby

Последние пару дней я работаю над гемом tax_cloud и с радостью сообщаю о версии 0.2.0, выпущенной сегодня. Драгоценный камень был создан @tempelmeyer и в настоящее время является зрелой оболочкой для службы расчета налога с продаж TaxCloud US .

Эта библиотека также является хорошим примером универсальной клиентской оболочки SOAP в Ruby. Я хотел указать на несколько успешных шаблонов для этой интеграции, которые я не могу взять на себя по большей части.

Обработка ошибок

Я заимствовал обработку ошибок из Mongoid @modetojoy . Сегодня кто-то сказал: «У меня была ошибка в спецификации, и Дурран сказал мне, как исправить ее в сообщении об ошибке». Правдивая история. Для этого мы определяем базовую ошибку, которая содержит проблему, сводку и решение. В случае tax_cloud это  TaxCloud :: Errors :: TaxCloudError в паре с config / locales / en.yml , файлом локали, который выполняет форматирование ошибок. Чтобы код ошибки нашел сообщение, нужно сделать две вещи: добавить файл локали в путь загрузки в tax_cloud.rb и немного отформатировать с помощью I18n.

    I18n.load_path << File.join(File.dirname(__FILE__), "config", "locales", "en.yml")

    def translate(key, options)
        ::I18n.translate("#{BASE_KEY}.#{key}", { :locale => :en }.merge(options)).strip
    end

Как выглядит ошибка в tax_cloud?

    Problem:
      Missing configuration.
    Summary:
      TaxCloud requires an API login ID and key.
    Resolution:
      Create a TaxCloud merchant account at http://www.taxcloud.net.
      Add a website to your TaxCloud account.
      This will generate an API ID and API Key that you will need to use the service.
      Configure the TaxCloud gem. For example, add the following to `config/initializers/tax_cloud.rb`.
      TaxCloud.configure do |config|
       config.api_login_id = 'your_tax_cloud_api_login_id'
       config.api_key = 'your_tax_cloud_api_key'
       config.usps_username = 'your_usps_username' # optional
      end

Довольно круто

Безопасные SOAP-запросы Gem tax_cloud использует Savon для создания SOAP-запросов. «Савон» по-французски означает «мыло», что сбивает с толку говорящих по-французски, таких как я, пытающихся объяснить, что SOAP — это Савон. В любом случае, клиент инициализируется с его WSDL.

    module TaxCloud #:nodoc:
      class Client < Savon::Client
        def initialize
          super 'https://api.taxcloud.net/1.0/?wsdl'
        end
      end
    end

Во-первых, нам нужно добавить аутентификацию для каждого запроса, который требуется API. Мы можем отменить запрос.

    def request(method, body = {})
        super method, :body => body.merge(auth_params)
    end
     
    def auth_params
    {
        'apiLoginID' => TaxCloud.configuration.api_login_id,
        'apiKey' => TaxCloud.configuration.api_key
    }
    end

Во-вторых, мы хотим обработать ошибки SOAP и дать подробное объяснение ошибок SOAP в стиле Mongoid. Это ваш типичный блок с доходностью.

    def request(method, body = {})
      safe do
        super method, :body => body.merge(auth_params)
      end
    end
     
    def safe &block
      begin
        yield
      rescue Savon::SOAP::Fault => e
        raise TaxCloud::Errors::SoapError.new(e)
      end
    end

Полный код можно найти в client.rb. Сама ошибка анализируется в soap_error.rb — ошибки SOAP приходят в стандартном формате.

Анализ ответов

Теперь мы создадим красивое исключение при сбоях SOAP, но мы все равно должны защитить себя от непредвиденных данных или успешных запросов SOAP, которые возвращают ошибки API. Эта возможность — вещь, которую я больше всего ненавижу в SOAP (против REST) ​​- она ​​делает программирование клиента излишне сложным. Служба TaxCloud возвращает тело SOAP с различными значениями в key_response / key_result / response_type, где ключом будет имя вызванного метода (например, ping_response). Небольшое количество метапрограммирования может создать базовый класс, который может анализировать ответ и сопоставлять путь XML, вызывая при необходимости ошибки. Он может быть разделен на общий тип ответа и, наконец, на конкретные декларативные реализации, такие как ping или authorized.

Большинство сервисов имеют общий шаблон ответа, обобщая его, получая очень продуктивную структуру, где добавление поддержки новых вызовов практически не требует кода. И вы никогда не должны сообщать пользователю о том, что вы делаете запросы SOAP, и возвращать какой-либо необработанный объект SOAP. Вернуть предметно-ориентированные классы с атрибутами в случае успеха и в противном случае вызвать исключения.

Тестирование SOAP-запросов

Gem tax_cloud использует VCR для тестирования запросов SOAP. Это удивительно просто: используйте кассету (файл YAML), которая записывает ее при первом запросе. Второй раз используется содержимое файла, а HTTP-запросы не выполняются. Вы можете отфильтровать чувствительные ключи в конфигурации.

    require 'vcr'
     
    VCR.configure do |c|
      c.cassette_library_dir = 'test/cassettes'
      c.hook_into :webmock
      c.filter_sensitive_data('api-login-id')  { TaxCloud.configuration.api_login_id }
      c.filter_sensitive_data('api-key')       { TaxCloud.configuration.api_key }
      c.filter_sensitive_data('usps-username') { TaxCloud.configuration.usps_username }  
    end

    def test_ping_with_invalid_credentials
      assert_raise TaxCloud::Errors::ApiError do
       VCR.use_cassette('ping_with_invalid_credentials') do
        TaxCloud.client.ping
       end
      end
    end
     
    def test_ping_with_invalid_response
      e = assert_raise TaxCloud::Errors::UnexpectedSoapResponse do
       VCR.use_cassette('ping_with_invalid_response') do
        TaxCloud.client.ping
       end
      end
      assert_equal "Expected a value for `ping_result`.", e.problem
    end
     
    def test_ping
      VCR.use_cassette('ping') do
       response = TaxCloud.client.ping
       assert_equal "OK", response
      end
    end

Вы можете увидеть остальные тесты здесь.

в заключение

Дайте мне знать, если вы используете некоторые из этих идей, опубликуйте свои комментарии и предложения и, пожалуйста, помогите улучшить гем tax_cloud на Github.