diff --git a/lib/prestashopper.rb b/lib/prestashopper.rb index 880ccc4..9a407f3 100644 --- a/lib/prestashopper.rb +++ b/lib/prestashopper.rb @@ -2,8 +2,9 @@ require 'rest-client' require 'nokogiri' require 'prestashopper/api' -require 'prestashopper/product' require 'prestashopper/uri_handler' +require 'active_support/core_ext/string' +require 'active_support/core_ext/hash' # @author Alfredo Amatriain # diff --git a/lib/prestashopper/api.rb b/lib/prestashopper/api.rb index 6fb81f6..c571342 100644 --- a/lib/prestashopper/api.rb +++ b/lib/prestashopper/api.rb @@ -22,33 +22,65 @@ def initialize(url, key) # List resources that the API key can access # @return [Array] list of resources the API can access def resources - xml = @resources_res.get.body - xml_doc = Nokogiri::XML xml - nodes = xml_doc.xpath '/prestashop/api/*' - resources_list = [] - nodes.each{|n| resources_list << n.name.to_sym} - return resources_list - end - - # Get all products data - # @return [Array] list of products. Each product is represented by a hash with all its attributes. - def get_products - # /api/products returns XML with the IDs of each individual product - xml_products = @resources_res['products'].get.body - xml_products_doc = Nokogiri::XML xml_products - products_nodes = xml_products_doc.xpath '/prestashop/products/*/@id' - ids_list = [] - products_nodes.each{|n| ids_list << n.value} - - # GET each individual product to get the whole data - products = [] - ids_list.each do |id| - xml_product = @resources_res["products/#{id}"].get.body - product = Product.xml2hash xml_product - products << product + @resources ||= Nokogiri::XML(@resources_res.get.body).xpath('/prestashop/api/*').collect { |resource| resource.name.to_sym } + end + + def method_missing(method, *args, &block) + if method.to_s.match(/^get_/) + resource = method.to_s.sub(/^get_/,'').pluralize.to_sym + raise RestClient::MethodNotAllowed, 'You do not have access to this resource' unless resources.include?(resource) + + if method.to_s == method.to_s.singularize + get_resource(resource, collect_ids_for_resource(args), true) + else + ids = collect_ids_for_resources(args) + ids.present? ? get_resources(resource, ids) : get_resource_ids(resource) + end + else + super + end + end + + private + + def get_resource_ids(resource) + Nokogiri::XML(@resources_res[resource].get.body).xpath("/prestashop/#{resource}/*/@id").collect(&:value) + end + + def get_resource(resource, id, raise_not_found_exception = false) + resource_class_name = resource.to_s.classify + resource_class = "Prestashopper::#{resource_class_name}".safe_constantize || Prestashopper.const_set(resource_class_name, Class.new(OpenStruct)) + + begin + response = Nokogiri::XML(@resources_res[resource][id].get.body).remove_namespaces!.xpath("/prestashop/#{resource.to_s.singularize}") + JSON.parse(Hash.from_xml(response.to_s).values.first.to_json, object_class: resource_class) + rescue RestClient::NotFound + raise if raise_not_found_exception + nil end + end + + def get_resources(resource, ids) + ids.uniq.sort.collect { |id| get_resource(resource, id) }.compact + end + + def collect_ids_for_resource(args) + raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" unless args.one? + id = args.first + validate_resource_id_type(id) + id + end + + def collect_ids_for_resources(args) + return nil if args.none? + raise TypeError, 'Arguments must be a list of resource ids' unless args.is_a?(Array) + args.each { |id| validate_resource_id_type(id) } + args + end - return products + def validate_resource_id_type(id) + raise TypeError, 'Argument must be a valid resource id type' unless id.is_a?(Numeric) || id.is_a?(String) + id end end end diff --git a/lib/prestashopper/product.rb b/lib/prestashopper/product.rb deleted file mode 100644 index d661cd3..0000000 --- a/lib/prestashopper/product.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'active_support/core_ext/hash' - -module Prestashopper - - # Has methods to convert the XML returned from the API to a ruby hash - class Product - - # Convert a product XML returned by the Prestashop API to a ruby hash, more manageable - # @param xml [String] XML returned by the Prestashop API - # @return [Hash] the product converted to a hash representation - def self.xml2hash(xml) - xml_doc = Nokogiri::XML( xml).remove_namespaces! - # Strip surrounding tag - nodes = xml_doc.xpath '/prestashop/*' - product_xml = nodes.to_s - product_hash = Hash.from_xml product_xml - - return product_hash['product'] - end - end -end diff --git a/test/api_test.rb b/test/api_test.rb index 2af4dd8..c1682fa 100644 --- a/test/api_test.rb +++ b/test/api_test.rb @@ -6,18 +6,12 @@ def setup @url = 'http://my.prestashop.com' @url_regex = %r{my[.]prestashop[.]com.*} @key = 'VALID_KEY' - end + @resources = [:customers, :orders, :products] + @product_ids = ['1', '2'] - def test_get_resources xml = File.open(File.join __dir__, 'xml', 'resources.xml').read stub_request(:any, @url_regex).to_return body: xml - resources = Prestashopper::API.new(@url, @key).resources - assert_equal 3, resources.length - [:customers, :orders, :products].each {|s| assert_includes resources, s} - end - - def test_get_products xml_products = File.open(File.join __dir__, 'xml', 'products.xml').read stub_request(:any, %r{my[.]prestashop[.]com/api/products}).to_return body: xml_products @@ -26,10 +20,32 @@ def test_get_products xml_product_2 = File.open(File.join __dir__, 'xml', 'product_2.xml').read stub_request(:any, %r{my[.]prestashop[.]com/api/products/2}).to_return body: xml_product_2 + end + + def test_get_resources + resources = Prestashopper::API.new(@url, @key).resources + assert_equal @resources.length, resources.length + @resources.each { |resource| assert_includes resources, resource } + end + + def test_get_products + products_ids = Prestashopper::API.new(@url, @key).get_products + assert_equal @product_ids.length, products_ids.length + @product_ids.each { |id| assert_includes products_ids, id } + end + + def test_get_products_for_ids + products = Prestashopper::API.new(@url, @key).get_products(*@product_ids) + assert_equal @product_ids.length, products.length + products_ids = products.collect(&:id) + @product_ids.each { |id| assert_includes products_ids, id } + products.each { |product| assert_kind_of Prestashopper::Product, product } + end - products = Prestashopper::API.new(@url, @key).get_products - assert_equal 2, products.length - product_ids = products.map{|p| p['id']} - ['1','2'].each {|id| assert_includes product_ids, id} + def test_get_product_for_id + product_id = @product_ids.first + product = Prestashopper::API.new(@url, @key).get_product(product_id) + assert_kind_of Prestashopper::Product, product + assert_equal product.id, product_id end end