require 'net/http' require 'net/https' begin require 'xmlsimple' rescue LoadError begin require 'rubygems' require_gem 'xml-simple' rescue LoadError abort <<-MSG The 'xml-simple' library could not be loaded. If you have RubyGems installed, you can install xml-simple by doing "gem install xml-simple". MSG end end # Aligni Web-Services API # # This is a Ruby wrapper to the Aligni Web-Services API. class Aligni class Record #:nodoc: attr_reader :type def initialize(type, hash) @type = type @hash = hash end def [](name) name = dashify(name) case @hash[name] when Hash then @hash[name] = (@hash[name].keys.length == 1 && Array === @hash[name].values.first) ? @hash[name].values.first.map { |v| Record.new(@hash[name].keys.first, v) } : Record.new(name, @hash[name]) else @hash[name] end end def id @hash["id"] end def attributes @hash.keys end def respond_to?(sym) super || @hash.has_key?(dashify(sym)) end def method_missing(sym, *args) if args.empty? && !block_given? && respond_to?(sym) self[sym] else super end end def to_s "\#" end def inspect to_s end private def dashify(name) name.to_s.tr("_", "_") end end #----------------------------------------------------------------------- # CONTACTS #----------------------------------------------------------------------- def list_contacts records :get, "contact", "/contact" end def get_contact(id) record :get, "/contact/#{id}" end def delete_contact(id) command :delete, "/contact/#{id}" end def update_contact(id, params) record :put, "/contact/#{id}", :contact => params end def create_contact(params) record :post, "/contact", :contact => params end #----------------------------------------------------------------------- # INVENTORY LOCATIONS #----------------------------------------------------------------------- def list_inventory_locations records :get, "inventory_location", "/inventory_location" end def get_inventory_location(id) record :get, "/inventory_location/#{id}" end def create_inventory_location(params) record :post, "/inventory_location/", :inventory_location => params end def update_inventory_location(id, params) record :put, "/inventory_location/#{id}", :inventory_location => params end def delete_inventory_location(id) command :delete, "/inventory_location/#{id}" end #----------------------------------------------------------------------- # INVENTORY SUBLOCATIONS #----------------------------------------------------------------------- def get_inventory_sublocation(id) records :get, "inventory_sublocation", "/inventory_sublocation/#{id}" end def create_inventory_sublocation(params) record :post, "/inventory_sublocation/", :inventory_sublocation => params end def update_inventory_sublocation(id, params) record :put, "/inventory_sublocation/#{id}", :inventory_sublocation => params end def delete_inventory_location(id) command :delete, "/inventory_sublocation/#{id}" end #----------------------------------------------------------------------- # INVENTORY UNITS #----------------------------------------------------------------------- def create_inventory_unit(params) record :post, "/inventory_unit/", :inventory_unit => params end #----------------------------------------------------------------------- # MANUFACTURERS #----------------------------------------------------------------------- def list_manufacturers records :get, "manufacturer", "/manufacturer" end def get_manufacturer(id) record :get, "/manufacturer/#{id}" end def create_manufacturer(params) record :post, "/manufacturer/", :manufacturer => params end def update_manufacturer(id, params) record :put, "/manufacturer/#{id}", :manufacturer => params end def delete_manufacturer(id) command :delete, "/manufacturer/#{id}" end #----------------------------------------------------------------------- # NOTES #----------------------------------------------------------------------- def add_note_to_contact(contact_id, params) record :post, "/note?owner_type=Contact&owner_id=#{contact_id}", :note=>params end def add_note_to_part(part_id, params) record :post, "/note?owner_type=Part&owner_id=#{part_id}", :note=>params end def add_note_to_manufacturer(manufacturer_id, params) record :post, "/note?owner_type=Manufacturer&owner_id=#{manufacturer_id}", :note=>params end def add_note_to_vendor(vendor_id, params) record :post, "/note?owner_type=Vendor&owner_id=#{vendor_id}", :note=>params end def update_note(id, params) record :put, "/note/#{id}", :note => params end def delete_note(id) command :delete, "/note/#{id}" end #----------------------------------------------------------------------- # PARTS #----------------------------------------------------------------------- def list_parts records :get, "part", "/part" end def get_part(id) record :get, "/part/#{id}" end def create_part(params) record :post, "/part", :part => params end def update_part(id, params) record :put, "/part/#{id}", :part => params end def delete_part(id) command :delete, "/part/#{id}" end #----------------------------------------------------------------------- # PARTTYPES #----------------------------------------------------------------------- def list_parttypes records :get, "parttype", "/parttype" end def get_parttype(id) record :get, "/parttype/#{id}" end def create_parttype(params) record :post, "/parttype/", :parttype => params end def update_parttype(id, params) record :put, "/parttype/#{id}", :parttype => params end def delete_parttype(id) command :delete, "/parttype/#{id}" end #----------------------------------------------------------------------- # QUOTES #----------------------------------------------------------------------- def create_quote(params) command :post, "/quote", :quote => params end def delete_quote(id) command :delete, "/quote/#{id}" end #----------------------------------------------------------------------- # SUBPARTS #----------------------------------------------------------------------- def create_subpart(params) record :post, "/subpart", :subpart => params end def update_subpart(id, params) record :put, "/subpart/#{id}", :subpart => params end def delete_subpart(id) command :delete, "/subpart/#{id}" end #----------------------------------------------------------------------- # UNITS #----------------------------------------------------------------------- def list_units records :get, "unit", "/unit" end def get_unit(id) record :get, "/unit/#{id}" end def create_unit(params) record :post, "/unit", :unit => params end def update_unit(id, params) record :put, "/unit/#{id}", :unit => params end def delete_unit(id) command :delete, "/unit/#{id}" end def create_unit_conversion(params) command :post, "/unit_conversion", :unit_conversion => params end def delete_unit_conversion(id) command :delete, "/unit_conversion/#{id}" end #----------------------------------------------------------------------- # VENDORS #----------------------------------------------------------------------- def list_vendors records :get, "vendor", "/vendor" end def get_vendor(id) record :get, "/vendor/#{id}" end def create_vendor(params) record :post, "/vendor", :vendor => params end def update_vendor(id, params) record :put, "/vendor/#{id}", :vendor => params end def delete_vendor(id) command :delete, "/vendor/#{id}" end def create_linecard(manufacturer_id, vendor_id) command :post, "/linecard", :post=>{:manufacturer_id => manufacturer_id, :vendor_id => vendor_id} end def delete_linecard(manufacturer_id, vendor_id) command :delete, "/linecard?manufacturer_id=#{manufacturer_id}&vendor_id=#{vendor_id}" end #----------------------------------------------------------------------- # VENDOR_PARTNUMBERS #----------------------------------------------------------------------- def create_vendor_partnumber(params) command :post, "/vendor_partnumber", :vendor_partnumber => params end def delete_vendor_partnumber(id) command :delete, "/vendor_partnumber/#{id}" end #----------------------------------------------------------------------- # Aligni #----------------------------------------------------------------------- def request(method, path, parameters = {}, second_try = false) headers = { "Accept" => "application/xml", "Content-Type" => "application/xml" } case method when :get then request = Net::HTTP::Get.new(path, headers) when :post then request = Net::HTTP::Post.new(path, headers) when :put then request = Net::HTTP::Put.new(path, headers) when :delete then request = Net::HTTP::Delete.new(path, headers) end response = @connection.request(request, parameters.to_xml) if response.code.to_i / 100 == 2 if response.body && response.body.size > 1 result = XmlSimple.xml_in(response.body, 'keeproot' => true, 'contentkey' => '__content__', 'forcecontent' => true) typecast_value(result) else result = true end else raise "#{response.message} (#{response.code})" end end def initialize(url, api_token, use_ssl=true) connect!(url, api_token, use_ssl) end def record(method, path, parameters={}) result = request(method, "/api/#{@api_token}#{path}", parameters) (result && !result.empty?) ? Record.new(result.keys.first, result.values.first) : nil end def records(method, node, path, parameters={}) result = request(method, "/api/#{@api_token}#{path}", parameters).values.first or return [] result = result[node] or return [] result = [result] unless Array === result result.map { |row| Record.new(node, row) } end def command(method, path, parameters={}) result = request(method, "/api/#{@api_token}#{path}", parameters) end private def connect!(url, api_token, use_ssl=true) @url = url @api_token = api_token @use_ssl = use_ssl @connection = Net::HTTP.new(url, @use_ssl ? 443 : 80) @connection.use_ssl = @use_ssl @connection.verify_mode = OpenSSL::SSL::VERIFY_NONE if @use_ssl end def typecast_value(value) case value when Hash if value.has_key?("__content__") content = translate_entities(value["__content__"]).strip case value["type"] when "integer" then content.to_i when "boolean" then content == "true" when "datetime" then Time.parse(content) when "date" then Date.parse(content) else content end else value.empty? ? nil : value.inject({}) do |h,(k,v)| h[k] = typecast_value(v) h end end when Array value.map! { |i| typecast_value(i) } case value.length when 0 then nil when 1 then value.first else value end else raise "can't typecast #{value.inspect}" end end def translate_entities(value) value.gsub(/</, "<"). gsub(/>/, ">"). gsub(/"/, '"'). gsub(/'/, "'"). gsub(/&/, "&") end end class Symbol def [](*args) to_s[*args] end end class Hash def to_xml XmlSimple.xml_out(self, 'keeproot' => true, 'noattr' => true) end end