require 'restclient' require 'active_support/all' module RailsConnector # Provides a simple wrapper for the CMS Rest API # using the credentials from {RailsConnector::Configuration.cms_api_options}. # # @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 cattr_accessor :credentials 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 # @param [Hash] value # @return [void] def self.credentials=(value) @@credentials = value.symbolize_keys 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] = credentials[:http_host] if credentials[:http_host].present? { :user => credentials[:login], :password => credentials[:api_key], :headers => headers, } end def url(resource_path) "#{credentials[:url]}/#{resource_path}" end end end end