lib/zuora/client.rb in zuora-ruby-0.2.0 vs lib/zuora/client.rb in zuora-ruby-0.3.0

- old
+ new

@@ -1,139 +1,129 @@ -# encoding: utf-8 require 'faraday' require 'faraday_middleware' -require 'json' +require 'nokogiri' module Zuora # Unable to connect. Check username / password - ConnectionError = Class.new StandardError + SoapConnectionError = Class.new StandardError # Non-success response - ErrorResponse = Class.new StandardError + SoapErrorResponse = Class.new StandardError class Client - attr_reader :connection + attr_accessor :session_token + SOAP_API_URI = '/apps/services/a/74.0'.freeze + SESSION_TOKEN_XPATH = + %w(//soapenv:Envelope soapenv:Body api:loginResponse + api:result api:Session).join('/').freeze + # Creates a connection instance. - # Makes an initial HTTP request to fetch session token. - # Subsequent requests made with .get, .post, and .put - # contain the authenticated session id in their headers. + # Makes an initial SOAP request to fetch session token. + # Subsequent requests contain the authenticated session id + # in headers. # @param [String] username # @param [String] password # @param [Boolean] sandbox - # @return [Zuora::Client] with .connection, .put, .post - def initialize(username, password, sandbox = false) - base_url = api_url sandbox - conn = connection base_url + # @return [Zuora::SoapClient] + def initialize(username, password, sandbox = true) + @username = username + @password = password + @sandbox = sandbox + end - response = auth_request conn, username, password + # Makes auth request, handles response + # @return [Faraday::Response] + def authenticate! + auth_response = call! :login, + username: @username, + password: @password - handle_response response, conn + handle_auth_response auth_response + rescue Object => e + raise SoapConnectionError, e end - # @param [String] url - URL of request - # @return [Faraday::Response] A response, with .headers, .status & .body - def get(url) - @connection.get do |req| - set_request_headers! req, url - end - end + # Fire a request + # @param [Xml] body - an object responding to .xml + # @return [Zuora::Response] + def request!(body) + fail 'body must support .to_xml' unless body.respond_to? :to_xml - # @param [String] url - URL for HTTP POST request - # @param [Params] params - Data to be sent in request body - # @return [Faraday::Response] A response, with .headers, .status & .body - def post(url, params) - response = @connection.post do |req| - set_request_headers! req, url - req.body = JSON.generate params + raw_response = connection.post do |request| + request.url SOAP_API_URI + request.headers['Content-Type'] = 'text/xml' + request.body = body.to_xml end - response - # if response.body['success'] - # return response - # else - # raise ErrorResponse.new(response) - # end + Zuora::Response.new(raw_response) end - # @param [String] url - URL for HTTP PUT request - # @param [Params] params - Data to be sent in request body - # @return [Faraday::Response] A response, with .headers, .status & .body - def put(url, params) - response = @connection.put do |request| - set_request_headers! request, url - request.body = JSON.generate params - end - - response - # if response.body['success'] - # return response - # else - # raise ErrorResponse.new(response) - # end + # The primary interface via which users should make SOAP requests. + # client.call :create, object_name: :BillRun, data: {...} + # client.call :subscribe, account: {...}, sold_to_contact: {...} + # @param [Symbol] call_name - one of :create, :subscribe, :amend, :update + # @return [Faraday:Response] - response + def call!(call_name, *args) + factory = Zuora::Dispatcher.send call_name + xml_builder = factory.new(*args).xml_builder + request_data = envelope_for call_name, xml_builder + request! request_data end private - # Make connection attempt - # @param [Faraday::Connection] conn - # @param [String] username - # @param [String] password - def auth_request(conn, username, password) - conn.post do |request| - set_auth_request_headers! request, username, password + # Generate envelope for request + # @param [Symbol] call name - one of the supported calls (see #call) + # @param [Callable] builder_modifier - function taking a builder + # @return [Nokogiri::XML::Builder] + def envelope_for(call_name, xml_builder_modifier) + if call_name == :login + Zuora::Utils::Envelope.xml(nil, xml_builder_modifier) + else + Zuora::Utils::Envelope.authenticated_xml(@session_token) do |b| + xml_builder_modifier.call b + end end end - # Sets instance variables or throws Connection error - # @param [Faraday::Response] response - # @param [Faraday::Connection] conn - def handle_response(response, conn) - if response.status == 200 - @auth_cookie = response.headers['set-cookie'].split(' ')[0] - @connection = conn + # Handle auth response, setting session + # @params [Faraday::Response] + # @return [Faraday::Response] + # @throw [SoapErrorResponse] + def handle_auth_response(response) + if response.raw.status == 200 + @session_token = extract_session_token response else - fail ConnectionError, response.body['reasons'] + message = 'Unable to connect with provided credentials' + fail SoapErrorResponse, message end + response end - # @param [Faraday::Request] request - Faraday::Request builder - # @param [String] username - Zuora username - # @param [String] password - Zuora password - def set_auth_request_headers!(request, username, password) - request.url '/rest/v1/connections' - request.headers['apiAccessKeyId'] = username - request.headers['apiSecretAccessKey'] = password - request.headers['Content-Type'] = 'application/json' + # Extracts session token from response and sets instance variable + # for use in subsequent requests + # @param [Faraday::Response] response - response to auth request + def extract_session_token(response) + Nokogiri::XML(response.raw.body).xpath( + SESSION_TOKEN_XPATH, Zuora::NAMESPACES + ).text end - # @param [Faraday::Request] request - Faraday Request builder - # @param [String] url - Relative URL for HTTP request - # @return [Nil] - def set_request_headers!(request, url) - request.url url - request.headers['Content-Type'] = 'application/json' - request.headers['Cookie'] = @auth_cookie - end - - # @param [String] url - # @return [Faraday::Client] - def connection(url) - Faraday.new(url, ssl: { verify: false }) do |conn| - conn.request :json - conn.response :json, content_type: /\bjson$/ - conn.use :instrumentation + # Initializes a connection using api_url + # @return [Faraday::Connection] + def connection + Faraday.new(api_url, ssl: { verify: false }) do |conn| conn.adapter Faraday.default_adapter end end - # @param [Boolean] sandbox - Use the sandbox url? - # @return [String] the API url - def api_url(sandbox) - if sandbox - Zuora::SANDBOX_URL + # @return [String] - SOAP url based on @sandbox + def api_url + if @sandbox + 'https://apisandbox.zuora.com/apps/services/a/74.0' else - Zuora::API_URL + 'https://api.zuora.com/apps/services/a/74.0' end end end end