Последние пару дней я работаю над гемом 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.