# encoding: utf-8 require 'httpclient' require 'json' require_relative 'communication' require_relative 'urls' require_relative 'groups' require_relative 'dto' module MxHero::API # The class that contains the response information # # The *code* represent the HTTP code of the response. # # The *msg* represent a hash with the response information. Example: # # { status: 500, code: 500, # developerMessage: "rules.already.exists.for.component", # moreInfoUrl: "mailto:support@mxhero.com" } # class Response < Struct.new(:code, :msg) # Response is successful? Based in HTTP status code def success? code.to_i == 200 || code.to_i == 201 end alias_method :content, :msg alias_method :content=, :msg= end class Account attr_accessor :account, :domain, :created_date, :updated_date, :group, :properties def initialize(props = {}) @account = props[:account] @domain = props[:domain] @created_date = props[:created_date] || props[:createdDate] @updated_date = props[:updated_date] || props[:updatedDate] @group = props[:group] @properties = props[:properties] || [] end def to_json { account: account, domain: domain, createdDate: created_date, updatedDate: updated_date, group: group, properties: properties }.to_json end def to_s """ account: #{account} domain: #{domain} updated_date: #{updated_date} created_date: #{created_date} group: #{group} properties: #{properties} """ end end # A client to interact with mxhero engine API class Client include Communication include Urls # @param [Hash] config the options of configuration # @option config [String] :api_url The URL to consume the API # @option config [String] :username The username for access the API # @option config [String] :password The password for the user that access the API # @option config [Boolean] :verbose (false) If true puts information about http operations def initialize(config = {}) @service_url = config[:api_url] @username = config[:username] @password = config[:password] @verbose = config[:verbose] || false end # Find a rule by domain and ID # # @param domain [String] # @param id [Integer] the rule id # # @return [Hash, nil] the Rule information or nil if not exist # # @raise an exception when the status code isn't 200 def domain_rule(domain, id) url = domain_rule_url(domain, id) response = call(:get, url) raise 'an error ocurred when try to communicate with the API' if response.status != 200 json_parse(response.content) end # Update a rule # @return [MxHero::API::Response] When the rule is update correctly then return MxHero::API::Response object without msg (msg nil) # In case of error, the message is completed with the cause of error def update_rule(rule) url = domain_rule_url(rule[:domain], rule[:id]) response = call(:put, url, rule.to_json) parse_response(response) end # Retrive all the domains # # TODO: Improve the response. We really need manage pagination here? # # @return [Hash] with the list of domains # * :elements [Array] the list of domains as a Hash, when any element contains: # * :domain [String] # * :server [String] # * :creationDate [Fixnum] # * :updateDate [Fixnum] # * :aliases # * :ldap # * :totalElements # * :totalPages # * :actualPage # # @raise an exception when the status code isn't 200 def domains response = call(:get, domains_url) raise 'an error ocurred when try to communicate with the API' if response.status != 200 response_as_a_hash = json_parse(response.content) response_as_a_hash end # Retrive the domain by id according to the parameter if exist # # @param id The domain name or id. For example: 'mydomain.com' # @return [Hash] with the domain attributes if exist # * :domain [String] # * :server [String] # * :creationDate [Fixnum] # * :updateDate [Fixnum] # * :aliases # * :ldap # @raise an exception when the status code is one of the 5xx status error # @raise an exception if @param id is nil def domain_by_id(id = nil) raise 'Id domain could not be nil' if id.nil? response = call(:get, domain_by_id_url(id)) response_as_a_hash = json_parse(response.content) response_as_a_hash end # # Retrive all the account from one domain # # @params [String] domain # @params [String] filter_account the account to filter in the list. This can be a part of an account. # @param [Hash] refinement # @option refinement [Fixnum] :limit of elements per page # @option refinement [Fixnum] :offset number of page (start in 1) # @option refinement [String] :account filter accounts that start with this value # @option refinement [Boolean] :without_group filter accounts without group # # # @return [Hash] with the following elements: # * :elements [Array] the list of accounts as a Hash, when any element contains: # * :account [String] example: alex # * :domain [String] example: mxhero.com # * :createdDate [Fixnum] example: 1375909565000 (epoch format) # * :updatedDate [Fixnum] # * :group [String] # * :aliases [Array] the list of aliases of one account # * :name [String] # * :domain [String] # * :dataSource [String] # * :dataSource [String] # * :totalElements [Fixnum] # * :totalPages [Fixnum] # * :actualPage [Fixnum] # # @raise an exception when the status code isn't 200 def accounts_by_domain(domain, refinement = {}) #filter_account = nil, pagination = {}) params = refinement.dup filter_account = params.delete(:account) without_group = params.delete(:without_group) || false limit, offset = params.values_at(:limit, :offset) if without_group url = accounts_without_group_url(domain, filter_account) else url = paginate accounts_by_domain_url(domain, filter_account), { limit: limit, offset: offset } end response = call(:get, url) json_parse(response.content) end def accounts_without_group_url(domain, filter_account) domain_by_id_url(domain) + "/groups/accounts/available?account=#{filter_account}" end def verbose? @verbose end def verbose=(verbose) @verbose = verbose end # @param domain [String] # @param [Hash] msg # @option msg [String] :domain # @option msg [Boolean] :twoWays # @option msg [Boolean] :enabled # @option msg [String] :name # @option msg [Integer] :created in epoch format # @option msg [Hash] :fromDirection # @option msg [Hash] :toDirection # @option msg [Array] :properties # @option msg [String] :component # # @return [MxHero::API::Response] def create_rule_for_domain(domain, msg) url = rules_for_domain_url(domain) response = call(:post, url, msg.to_json, throw_exception: false) parse_response(response) end ## Create a rule ## ## @param [Hash] msg ## @option msg [String] :domain ## @option msg [Boolean] :twoWays ## @option msg [Boolean] :enabled ## @option msg [String] :name ## @option msg [Integer] :created in epoch format ## @option msg [Hash] :fromDirection ## @option msg [Hash] :toDirection ## @option msg [Array] :properties ## @option msg [String] :component ## ## [MxHero::API::Response] #def create_rule(msg) # response = call(:post, rules_url, msg.to_json, throw_exception: false) # parse_response(response) #end # Retrieve all the rules for one domain # # @param [String] domain # @param [String] component Filter the list of rules by this component [optional] # # @return [MxHero::API::Response] reponse # the key :msg contains an array of rules for the domain. def rules_for_domain(domain, component = nil) url = rules_for_domain_url(domain) url << "?component=#{component}" if component response = call(:get, url) parse_response(response, on_empty: []) end # @return [Boolean] true when operation it's ok def delete_rule(domain, id) url = domain_rule_url(domain, id) response = call(:delete, url) return true if response.status == 200 return false end # @param [String] domain # @param [String] account Ex.: test or mxhero (the user name) # @return [MxHero::API::Response] reponse # the key :msg contains a Hash with the key, value of any property. # Example: # # { 'email': 'test@mxhero.com', 'name': 'John', 'lastname': 'Doe', ... } # def account_properties(domain, account) url = account_properties_url(domain, account) response = call(:get, url) if response.status == 200 props = {} json_parse(response.content).each { |property| props[property[:name]] = property[:value] } return Response.new(response.code, props) end parse_response(response) end # @param [String] domain # @param [String] account # @param [Hash] properties The properties of the account. Ex.: { 'email': 'test@mxhero.com', 'name': 'John', 'lastname': 'Doe', ... } # # @return [MxHero::API::Response]. On success not return msg. def update_account_properties(domain, account, properties) url = account_properties_url(domain, account) response = call(:put, url, account_properties_to_json(properties)) parse_response(response) end # # @param [String] domain # @param [Array] accounts. An array of hash with :account and :properties. The :properties # contains a Hash with the equivalent of name and value. # @param [String] scope :groups, :properties or :both (default: properties) # def update_accounts(domain, accounts, scope = :properties) scope_param = scope == :both ? 'groups,properties' : scope.to_s #url = "/domains/#{domain}/accounts/upload?scope=#{scope_param}" url = accounts_by_domain_url(domain) + "/upload?scope=#{scope_param}" message = [] accounts.each do |account| properties = remap_properties(account[:properties]) message << { account: account[:account], properties: properties, group: account[:group], domain: domain } #message << { account: account[:account], properties: properties, group: account[:group] } end response = call(:put, url, message.to_json) # accounts to json parse_response(response) end # -------------------------------------------------------------------------------------------------------------------------------- private def remap_properties(properties) return nil if properties.nil? properties.keys.map { |name| { name: name, value: properties[name] } } end # Complete the URL with the pagination info (:limit and :offset) # @param [String] original URL. Ex.: http://www.example.com/api/accounts # @param [Hash] pagination # @option pagination [Fixnum] :limit of elements per page # @option pagination [Fixnum] :offset number of page (start in 1) # # @return [String] the URL with the pagination parameters. # Ex.: url: http://www.example.com/api/accounts # pagination: { limit: 4, offset: 2 } # return > http://www.example.com/api/accounts?limit=4&offset=2 def paginate(url, pagination) paginated = url.dup connector = url.include?('?') ? '&' : '?' [ :limit, :offset ].map do |elem| if pagination.key? elem paginated << "#{connector}#{elem.to_s}=#{pagination[elem]}" connector = '&' end end paginated end def account_properties_to_json(properties) out = [] properties.each do |key, value| out << { name: key, value: value } end out.to_json end # @return [MxHero::API::Response] a response object def parse_response(response, opts = { on_empty: nil }) json = response.content hash = json.nil? || json.empty? ? opts[:on_empty] : json_parse(json) Response.new(response.code, hash) end def rules_for_domain_url(domain) service_url + "/domains/#{domain}/rules" end def rules_url service_url + '/rules' end def accounts_by_domain_url(domain, filter_account = nil) filter = filter_account ? "?account=#{filter_account}" : '' domain_by_id_url(domain) + 'accounts' + filter end def domain_rule_url(domain, id) rules_for_domain_url(domain) + "/#{id}" end def account_properties_url(domain, account) accounts_by_domain_url(domain) + "/#{account}/properties" end end end