require 'restclient' require 'active_support/all' module RailsConnector # Provides a simple wrapper for the CMS Rest API. # # @example Request the published workspace: # RailsConnector::CmsRestApi.get('workspaces/published') # # @example Create a new Obj: # RailsConnector::CmsRestApi.post('revisions/001384beff9e5845/objs', # {'obj' => {'_path' => '/new_obj', '_obj_class' => 'Publication'}}) # # @example Update an Obj: # RailsConnector::CmsRestApi.put('revisions/001384beff9e5845/objs/9e432077f0412a63', # {'obj' => {'title' => 'new title'}}) # # @example Delete an Obj: # RailsConnector::CmsRestApi.delete('revisions/001384beff9e5845/objs/f4123622ff07b70b') # # @example Specify a poll interval (in seconds; default: 2) to use in case the response # is a task reference response and the final response is polled for: # RailsConnector::CmsRestApi.put('workspace/rtc/publish', nil, :interval => 10) # # @example Return immediately with the first response (without polling in case it is a # task reference response): # RailsConnector::CmsRestApi.task_unaware_request(:put, 'workspace/rtc/publish', nil) # class CmsRestApi class Configuration attr_accessor :url attr_accessor :login attr_accessor :api_key attr_accessor :http_host attr_accessor :tenant_name def url if @url.nil? raise RailsConnectorError, 'CmsRestApi configuration key "url" is missing. '\ 'Set "RailsConnector::Configuration.cms_url = "' end @url end end cattr_accessor :configuration do Configuration.new end def self.get(resource_path, payload = nil, options = nil) request_cms_api(:get, resource_path, payload, options) end def self.put(resource_path, payload, options = nil) request_cms_api(:put, resource_path, payload, options) end def self.post(resource_path, payload, options = nil) request_cms_api(:post, resource_path, payload, options) end def self.delete(resource_path, payload = nil, options = nil) request_cms_api(:delete, resource_path, payload, options) end def self.task_unaware_request(method, resource_path, payload = nil) raise "Unexpected method #{method}" unless [:delete, :get, :post, :put].include?(method) response_for_request_cms_api(method, resource_path, payload) end class << self private def request_cms_api(action, resource_path, payload, options) decoded = response_for_request_cms_api(action, resource_path, payload) return decoded unless Hash === decoded return decoded unless decoded.keys == ["task"] task_data = decoded["task"] return decoded unless Hash === task_data task_path = "tasks/#{task_data["id"]}" final_response(task_path, options) end def response_for_request_cms_api(action, resource_path, payload) request_params = basic_request_params request_params[:method] = action request_params[:url] = url(resource_path) request_params[:payload] = MultiJson.encode(payload) if payload.present? MultiJson.decode(RestClient::Request.execute(request_params)) rescue RestClient::ExceptionWithResponse => e http_code = e.http_code if http_code == 403 raise AccessDenied.new(e.http_body) elsif 400 <= http_code && http_code < 500 begin specific_output = MultiJson.decode(e.http_body)['error'] rescue MultiJson::DecodeError # fall through else raise ClientError.new(specific_output, http_code) end end raise BackendNotAvailable.new(e.http_body, http_code) end def final_response(task_path, options) options ||= {} wait = options[:interval].presence.try(:to_f) || 2 task_data = response = nil loop do sleep wait task_data = response_for_request_cms_api(:get, task_path, nil) break unless task_data["status"] == "open" end return task_data["result"] if task_data["status"] == "success" message = task_data["message"] || "Missing error message in task response #{task_data}" raise ClientError.new(message, 400) end def basic_request_params headers = { :content_type => :json, :accept => :json, } headers[:host] = configuration.http_host if configuration.http_host.present? { :user => configuration.login, :password => configuration.api_key, :headers => headers, } end def url(resource_path) "#{configuration.url}/#{resource_path}" end end end end