lipseys 5.0.1 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 823c554eff146611688ad459e698e449625640f8
4
- data.tar.gz: 96a39a440393b8bebcb8a4b7872aeaf6a0cf893b
3
+ metadata.gz: 4403d00b0a7ca5e2b6754e739d85bae695e8c6ce
4
+ data.tar.gz: c8e997c51bc28411ea030341b77591a20e6f9b58
5
5
  SHA512:
6
- metadata.gz: 7a02161b0af4d5dc95f45b9f0a677b5ff7379ad20c5a59b5fd02ae5101adfa7c0b715d986eded166b27fc24e8a34f0c7c3f6e68da468f10aeb93755b77d40e10
7
- data.tar.gz: c1592da12a360a88b85daef78e24ddec07858a1e3688e12b7d30cd7dfc6873281b5dce54cadbcf490bd65287115053bab4ce1ab9b348e9af8c032ed02866f54f
6
+ metadata.gz: e5c1ad6a782c66656e2dafe7f4dedc0fecf2232e0c5425319d7b5ac0a775633ab4a9dcb4f9f7461a29407e713492e3c977fdf58ffabc4f46ad9649e02dbf2dd7
7
+ data.tar.gz: 5589846a2cbbee7b70eaa57831b42d385a09ba42950d1205162c42bb033070abadcb880b4d96daaf3821cb47154abe9c45c131ddc2fff2a30643774d2b5173fa
@@ -1,20 +1,38 @@
1
- require 'lipseys/version'
2
-
3
- require 'net/http'
4
- require 'nokogiri'
5
- require 'savon'
6
-
7
1
  require 'lipseys/base'
8
- require 'lipseys/soap_client'
2
+ require 'lipseys/version'
9
3
 
4
+ require 'lipseys/api'
10
5
  require 'lipseys/catalog'
6
+ require 'lipseys/client'
7
+ require 'lipseys/error'
11
8
  require 'lipseys/inventory'
12
- require 'lipseys/invoice'
9
+ require 'lipseys/items'
13
10
  require 'lipseys/order'
11
+ require 'lipseys/response'
14
12
  require 'lipseys/user'
15
- require 'lipseys/image'
16
- require 'lipseys/parser'
17
13
 
18
14
  module Lipseys
19
- class NotAuthenticated < StandardError; end
15
+
16
+ class << self
17
+ attr_accessor :config
18
+ end
19
+
20
+ def self.config
21
+ @config ||= Configuration.new
22
+ end
23
+
24
+ def self.configure
25
+ yield(config)
26
+ end
27
+
28
+ class Configuration
29
+ attr_accessor :proxy_address
30
+ attr_accessor :proxy_port
31
+
32
+ def initialize
33
+ @proxy_address ||= nil
34
+ @proxy_port ||= nil
35
+ end
36
+ end
37
+
20
38
  end
@@ -0,0 +1,121 @@
1
+ require 'net/http'
2
+
3
+ module Lipseys
4
+ module API
5
+
6
+ ROOT_API_URL = 'https://api.lipseys.com/api/integration'.freeze
7
+ USER_AGENT = "LipseysRubyGem/#{Lipseys::VERSION}".freeze
8
+
9
+ FILE_UPLOAD_ATTRS = {
10
+ permitted: %i( file_name file_type file_contents ).freeze,
11
+ reqired: %i( file_type file_contents ).freeze,
12
+ }
13
+
14
+ def get_request(endpoint, headers = {})
15
+ request = Net::HTTP::Get.new(request_url(endpoint))
16
+
17
+ submit_request(request, {}, headers)
18
+ end
19
+
20
+ def post_request(endpoint, data = {}, headers = {})
21
+ request = Net::HTTP::Post.new(request_url(endpoint))
22
+
23
+ submit_request(request, data, headers)
24
+ end
25
+
26
+ def post_file_request(endpoint, file_data, headers = {})
27
+ request = Net::HTTP::Post.new(request_url(endpoint))
28
+
29
+ submit_file_request(request, file_data, headers)
30
+ end
31
+
32
+ private
33
+
34
+ def submit_request(request, data, headers)
35
+ set_request_headers(request, headers)
36
+
37
+ request.body = data.is_a?(Hash) ? data.to_json : data
38
+
39
+ process_request(request)
40
+ end
41
+
42
+ def submit_file_request(request, file_data, headers)
43
+ boundary = ::SecureRandom.hex(15)
44
+
45
+ headers.merge!(content_type_header("multipart/form-data; boundary=#{boundary}"))
46
+
47
+ build_multipart_request_body(request, file_data, boundary)
48
+ set_request_headers(request, headers)
49
+ process_request(request)
50
+ end
51
+
52
+ def process_request(request)
53
+ uri = URI(request.path)
54
+
55
+ response = Net::HTTP.start(uri.host, uri.port, Lipseys.config.proxy_address, Lipseys.config.proxy_port, use_ssl: true) do |http|
56
+ http.request(request)
57
+ end
58
+
59
+ Lipseys::Response.new(response)
60
+ end
61
+
62
+ def build_multipart_request_body(request, file_data, boundary)
63
+ file_type = file_data[:file_type]
64
+ file_contents = file_data[:file_contents]
65
+ file_name = file_data[:file_name] || "ffl-document.#{file_type}"
66
+ content_type = "application/#{file_data[:file_type]}"
67
+
68
+ body = []
69
+ body << "--#{boundary}\r\n"
70
+ body << "Content-Disposition: form-data; name=\"file\"; filename=\"#{file_name}\"\r\n"
71
+ body << "Content-Type: #{content_type}\r\n"
72
+ body << "\r\n"
73
+ body << "#{file_contents}\r\n"
74
+ body << "--#{boundary}--\r\n"
75
+
76
+ request.body = body.join
77
+ end
78
+
79
+ def set_request_headers(request, headers)
80
+ request['User-Agent'] = USER_AGENT
81
+
82
+ headers.each { |header, value| request[header] = value }
83
+ end
84
+
85
+ def auth_header(token)
86
+ { 'Token' => token }
87
+ end
88
+
89
+ def content_type_header(type)
90
+ { 'Content-Type' => type }
91
+ end
92
+
93
+ def request_url(endpoint)
94
+ [ROOT_API_URL, endpoint].join('/')
95
+ end
96
+
97
+ def standardize_body_data(submitted_data, permitted_data_attrs)
98
+ _submitted_data = submitted_data.deep_transform_keys(&:to_sym)
99
+ permitted_data = {}
100
+
101
+ _submitted_data.each do |k1, v1|
102
+ if permitted_data_attrs.include?(k1)
103
+ if v1.is_a?(Array)
104
+ permitted_data[k1] = []
105
+
106
+ v1.each do |sub_data|
107
+ permitted_sub_data = {}
108
+ sub_data.each { |k2, v2| permitted_sub_data[k2] = v2 if permitted_data_attrs.include?(k2) }
109
+ permitted_data[k1] << permitted_sub_data
110
+ end
111
+ else
112
+ permitted_data[k1] = v1
113
+ end
114
+ end
115
+ end
116
+
117
+ permitted_data.deep_transform_keys { |k| k.to_s.camelize(:lower) }
118
+ end
119
+
120
+ end
121
+ end
@@ -3,70 +3,24 @@ module Lipseys
3
3
 
4
4
  protected
5
5
 
6
- # Wrapper to `self.requires!` that can be used as an instance method.
7
6
  def requires!(*args)
8
7
  self.class.requires!(*args)
9
8
  end
10
9
 
11
10
  def self.requires!(hash, *params)
11
+ hash_keys = hash.collect { |k, v| v.is_a?(Array) ? [k, v.collect(&:keys)] : k }.flatten
12
+
12
13
  params.each do |param|
13
14
  if param.is_a?(Array)
14
- raise ArgumentError.new("Missing required parameter: #{param.first}") unless hash.has_key?(param.first)
15
+ raise ArgumentError.new("Missing required parameter: #{param.first}") unless hash_keys.include?(param.first)
15
16
 
16
17
  valid_options = param[1..-1]
17
18
  raise ArgumentError.new("Parameter: #{param.first} must be one of: #{valid_options.join(', ')}") unless valid_options.include?(hash[param.first])
18
19
  else
19
- raise ArgumentError.new("Missing required parameter: #{param}") unless hash.has_key?(param)
20
+ raise ArgumentError.new("Missing required parameter: #{param}") unless hash_keys.include?(param)
20
21
  end
21
22
  end
22
23
  end
23
24
 
24
- def content_for(xml_doc, field)
25
- node = xml_doc.css(field).first
26
- node.nil? ? nil : node.content.strip
27
- end
28
-
29
- def get_response_xml(api_url, params)
30
- uri = URI(api_url)
31
- uri.query = URI.encode_www_form(params_to_auth(params))
32
-
33
- response = Net::HTTP.get_response(uri)
34
- xml_doc = Nokogiri::XML(response.body)
35
-
36
- raise Lipseys::NotAuthenticated if not_authenticated?(xml_doc)
37
-
38
- xml_doc
39
- end
40
-
41
- def not_authenticated?(xml_doc)
42
- msg = content_for(xml_doc, 'CatalogError')
43
- msg =~ /Login failed/i || msg =~ /Credentials Not Valid/i
44
- end
45
-
46
- def stream_to_tempfile(api_url, params)
47
- tempfile = Tempfile.new
48
- uri = URI(api_url)
49
- uri.query = URI.encode_www_form(params_to_auth(params))
50
-
51
- Net::HTTP.get_response(uri) do |response|
52
- File.open(tempfile, 'w') do |file|
53
- response.read_body do |chunk|
54
- file.write(chunk.force_encoding('UTF-8'))
55
- end
56
- end
57
- end
58
-
59
- tempfile
60
- end
61
-
62
- private
63
-
64
- def params_to_auth(params)
65
- {
66
- email: params.fetch(:username),
67
- pass: params.fetch(:password),
68
- }
69
- end
70
-
71
25
  end
72
26
  end
@@ -1,87 +1,64 @@
1
1
  module Lipseys
2
2
  class Catalog < Base
3
3
 
4
- CHUNK_SIZE = 500
5
- API_URL = 'http://184.188.80.195/API/catalog.ashx'
6
- ITEMTYPES = %w(ACCESSORY FIREARM NFA OPTIC)
7
-
8
4
  def initialize(options = {})
9
5
  requires!(options, :username, :password)
10
6
 
11
- @options = options
7
+ @client = Lipseys::Client.new(username: options[:username], password: options[:password])
12
8
  end
13
9
 
14
10
  def self.all(options = {})
15
11
  requires!(options, :username, :password)
12
+
16
13
  new(options).all
17
14
  end
18
15
 
19
16
  def all
20
- inventory_tempfile = stream_to_tempfile(Lipseys::Inventory::API_URL, @options)
21
- catalog_tempfile = stream_to_tempfile(API_URL, @options)
22
- inventory = []
23
- items = []
24
-
25
- # Let's get the inventory and toss 'er into an array
26
- Lipseys::Parser.parse(inventory_tempfile, 'Item') do |node|
27
- inventory.push({
28
- item_identifier: content_for(node, 'ItemNo'),
29
- map_price: content_for(node, 'RetailMAP'),
30
- quantity: content_for(node, 'QtyOnHand'),
31
- price: content_for(node, 'Price')
32
- })
33
- end
34
-
35
- Lipseys::Parser.parse(catalog_tempfile, 'Item') do |node|
36
- item = map_hash(node)
37
- availability = inventory.select { |i| i[:item_identifier] == item[:item_identifier] }.first
38
-
39
- if availability
40
- item[:price] = availability[:price]
41
- item[:quantity] = availability[:quantity]
42
- item[:map_price] = availability[:map_price]
43
- end
44
-
45
- items << item
46
- end
47
-
48
- inventory_tempfile.unlink
49
- catalog_tempfile.unlink
50
-
51
- items
17
+ @client.items.catalog_feed[:data].map { |item| map_hash(item) }
52
18
  end
53
19
 
54
20
  private
55
21
 
56
- def map_hash(node)
57
- model = content_for(node, 'Model')
58
- mfg_number = content_for(node, 'MFGModelNo')
59
- name = "#{model} #{mfg_number}".lstrip.rstrip
60
-
22
+ def map_hash(item)
61
23
  {
62
- name: name,
63
- model: model,
64
- upc: content_for(node, 'UPC'),
65
- short_description: content_for(node, 'Desc2'),
66
- category: content_for(node, 'Type'),
67
- price: nil,
68
- weight: content_for(node, 'Weight'),
69
- item_identifier: content_for(node, 'ItemNo'),
70
- brand: content_for(node, 'MFG'),
71
- mfg_number: mfg_number,
24
+ name: [item[:model].try(:strip), item[:manufacturerModelNo].try(:strip)].compact.join(' '),
25
+ model: item[:model],
26
+ upc: item[:upc],
27
+ long_description: item[:description1],
28
+ short_description: item[:description2],
29
+ category: item[:type],
30
+ price: item[:price],
31
+ map_price: item[:retailMap],
32
+ msrp: item[:msrp],
33
+ quantity: item[:quantity],
34
+ weight: item[:weight],
35
+ item_identifier: item[:itemNo],
36
+ brand: item[:manufacturer],
37
+ mfg_number: item[:manufacturerModelNo],
72
38
  features: {
73
- caliber: content_for(node, 'Caliber'),
74
- action: content_for(node, 'Action'),
75
- barrel: content_for(node, 'Barrel'),
76
- capacity: content_for(node, 'Capacity'),
77
- finish: content_for(node, 'Finish'),
78
- length: content_for(node, 'Length'),
79
- receiver: content_for(node, 'Receiver'),
80
- safety: content_for(node, 'Safety'),
81
- sights: content_for(node, 'Sights'),
82
- magazine: content_for(node, 'Magazine'),
83
- chamber: content_for(node, 'Chamber')
84
- }
39
+ caliber: item[:caliberGauge],
40
+ action: item[:action],
41
+ barrel: item[:barrelLength],
42
+ capacity: item[:capacity],
43
+ finish: item[:finish],
44
+ length: item[:overallLength],
45
+ receiver: item[:receiver],
46
+ safety: item[:safety],
47
+ sights: item[:sights],
48
+ stock_frame_grips: item[:stockFrameGrips],
49
+ magazine: item[:magazine],
50
+ chamber: item[:chamber],
51
+ rate_of_twist: item[:rateOfTwist],
52
+ additional_feature_1: item[:additionalFeature1],
53
+ additional_feature_2: item[:additionalFeature2],
54
+ additional_feature_3: item[:additionalFeature3],
55
+ nfa_thread_pattern: item[:nfaThreadPattern],
56
+ nfa_attachment_method: item[:nfaAttachmentMethod],
57
+ nfa_baffle_type: item[:nfaBaffleType],
58
+ nfa_db_reduction: item[:nfaDbReduction],
59
+ nfa_form_3_caliber: item[:nfaForm3Caliber],
60
+ optic_magnification: item[:opticMagnification],
61
+ },
85
62
  }
86
63
  end
87
64
 
@@ -0,0 +1,40 @@
1
+ require 'lipseys/api'
2
+ require 'lipseys/items'
3
+ require 'lipseys/order'
4
+
5
+ module Lipseys
6
+ class Client < Base
7
+
8
+ include Lipseys::API
9
+
10
+ attr_accessor :access_token
11
+
12
+ def initialize(options = {})
13
+ requires!(options, :username, :password)
14
+ @options = options
15
+
16
+ authenticate!
17
+ end
18
+
19
+ def items
20
+ @items ||= Lipseys::Items.new(self)
21
+ end
22
+
23
+ def order
24
+ @order ||= Lipseys::Order.new(self)
25
+ end
26
+
27
+ private
28
+
29
+ def authenticate!
30
+ response = post_request(
31
+ 'authentication/login',
32
+ { email: @options[:username], password: @options[:password] },
33
+ content_type_header('application/json')
34
+ )
35
+
36
+ self.access_token = response[:token]
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+ module Lipseys
2
+ class Error < StandardError
3
+
4
+ class NoContent < Lipseys::Error; end
5
+ class NotAuthorized < Lipseys::Error; end
6
+ class NotFound < Lipseys::Error; end
7
+ class RequestError < Lipseys::Error; end
8
+ class TimeoutError < Lipseys::Error; end
9
+
10
+ end
11
+ end
@@ -1,62 +1,30 @@
1
1
  module Lipseys
2
2
  class Inventory < Base
3
3
 
4
- API_URL = 'http://184.188.80.195/API/pricequantitycatalog.ashx'
5
-
6
4
  def initialize(options = {})
7
5
  requires!(options, :username, :password)
8
6
 
9
- @options = options
7
+ @client = Lipseys::Client.new(username: options[:username], password: options[:password])
10
8
  end
11
9
 
12
10
  def self.all(options = {})
13
- new(options).all
14
- end
11
+ requires!(options, :username, :password)
15
12
 
16
- def self.quantity(options = {})
17
13
  new(options).all
18
14
  end
19
-
20
- def self.get_quantity_file(options = {})
21
- new(options).get_quantity_file
22
- end
15
+ class << self; alias_method :quantity, :all; end
23
16
 
24
17
  def all
25
- items = []
26
- tempfile = stream_to_tempfile(API_URL, @options)
27
-
28
- Lipseys::Parser.parse(tempfile, 'Item') do |node|
29
- _map_hash = map_hash(node)
30
-
31
- items << _map_hash unless _map_hash.nil?
32
- end
33
-
34
- tempfile.unlink
35
-
36
- items
37
- end
38
-
39
- def get_quantity_file
40
- quantity_tempfile = stream_to_tempfile(API_URL, @options)
41
- tempfile = Tempfile.new
42
-
43
- Lipseys::Parser.parse(quantity_tempfile, 'Item') do |node|
44
- tempfile.puts("#{content_for(node, 'ItemNo')},#{content_for(node, 'QtyOnHand')}")
45
- end
46
-
47
- quantity_tempfile.unlink
48
- tempfile.close
49
-
50
- tempfile.path
18
+ @client.items.pricing_quantity_feed[:data][:items].map { |item| map_hash(item) }
51
19
  end
52
20
 
53
21
  private
54
22
 
55
- def map_hash(node)
23
+ def map_hash(item)
56
24
  {
57
- item_identifier: content_for(node, 'ItemNo'),
58
- quantity: content_for(node, 'QtyOnHand'),
59
- price: content_for(node, 'Price')
25
+ item_identifier: item[:itemNumber],
26
+ quantity: item[:quantity],
27
+ price: item[:price],
60
28
  }
61
29
  end
62
30
 
@@ -0,0 +1,54 @@
1
+ require 'lipseys/api'
2
+
3
+ module Lipseys
4
+ class Items < Base
5
+
6
+ include Lipseys::API
7
+
8
+ ENDPOINTS = {
9
+ catalog_feed: "items/catalogfeed".freeze,
10
+ pricing_quantity_feed: "items/pricingquantityfeed".freeze,
11
+ validate_item: "items/validateitem".freeze,
12
+ catalog_feed_item: "items/catalogfeed/item".freeze,
13
+ }
14
+
15
+ def initialize(client)
16
+ @client = client
17
+ end
18
+
19
+ def catalog_feed
20
+ endpoint = ENDPOINTS[:catalog_feed]
21
+
22
+ get_request(endpoint, auth_header(@client.access_token))
23
+ end
24
+
25
+ def pricing_quantity_feed
26
+ endpoint = ENDPOINTS[:pricing_quantity_feed]
27
+
28
+ get_request(endpoint, auth_header(@client.access_token))
29
+ end
30
+
31
+ # `identifier` can be Item #, Mfg Model #, or UPC
32
+ def validate_item(identifier)
33
+ endpoint = ENDPOINTS[:validate_item]
34
+ headers = [
35
+ *auth_header(@client.access_token),
36
+ *content_type_header('application/json'),
37
+ ].to_h
38
+
39
+ post_request(endpoint, "'#{identifier}'", headers)
40
+ end
41
+
42
+ # `identifier` can be Item #, Mfg Model #, or UPC
43
+ def catalog_feed_item(identifier)
44
+ endpoint = ENDPOINTS[:catalog_feed_item]
45
+ headers = [
46
+ *auth_header(@client.access_token),
47
+ *content_type_header('application/json'),
48
+ ].to_h
49
+
50
+ post_request(endpoint, "'#{identifier}'", headers)
51
+ end
52
+
53
+ end
54
+ end
@@ -1,74 +1,55 @@
1
- module Lipseys
2
- # Required options when submitting an order:
3
- #
4
- # * `:email` and `:password`
5
- # * `:item_number` OR `:upc`
6
- # * `:quantity`
7
- # * `:purchase_order`
8
- #
9
- # Optional order params:
10
- #
11
- # * `:notify_by_email` (boolean)
12
- # * `:note`
13
- class Order < SoapClient
14
-
15
- def initialize(options = {})
16
- requires!(options, :username, :password, :quantity, :purchase_order)
17
- @email = options[:username]
18
- @password = options[:password]
19
- @quantity = options[:quantity]
20
-
21
- @notify_by_email = (options[:notify_by_email] == true ? 'Y' : nil)
22
- @purchase_order = options[:purchase_order]
23
- @note = options[:note]
24
-
25
- @item_number_or_upc = options[:item_number] || options[:upc]
26
- raise ArgumentError.new("Either :item_number or :upc must be given") if @item_number_or_upc.nil?
27
- end
28
-
1
+ require 'lipseys/api'
29
2
 
30
- def self.submit!(*args)
31
- new(*args).submit!
32
- end
33
-
34
- def submit!
35
- response = soap_client.call(:submit_order, message: build_order_data)
36
-
37
- raise Lipseys::NotAuthenticated if not_authenticated?(response)
38
-
39
- order_result = response.body[:submit_order_response][:submit_order_result]
40
-
41
- {
42
- order_number: order_result[:order_no],
43
- new_order: (order_result[:new_order] == 'Y'),
44
- success: (order_result[:success] == 'Y'),
45
- description: order_result[:return_desc],
46
- quantity_received: Integer(order_result[:qty_received]),
47
- }
48
- rescue Savon::Error => e
49
- { success: false, description: e.to_s }
3
+ module Lipseys
4
+ class Order < Base
5
+
6
+ include Lipseys::API
7
+
8
+ SUBMIT_TO_STORE_ATTRS = {
9
+ permitted: %i( po_number disable_email items item_no quantity note ).freeze,
10
+ required: %i( items item_no quantity ).freeze
11
+ }
12
+
13
+ SUBMIT_TO_DROP_SHIP_ATTRS = {
14
+ permitted: %i(
15
+ warehouse po_number billing_name billing_address_line_1 billing_address_line_2 billing_address_city
16
+ billing_address_state billing_address_zip shipping_name shipping_address_line_1 shipping_address_line_2
17
+ shipping_address_city shipping_address_state shipping_address_zip message_for_sales_exec disable_email
18
+ items item_no quantity note overnight
19
+ ).freeze,
20
+ required: %i(
21
+ po_number billing_name billing_address_line_1 billing_address_city billing_address_state billing_address_zip
22
+ shipping_name shipping_address_line_1 shipping_address_city shipping_address_state shipping_address_zip
23
+ items item_no quantity
24
+ ).freeze
25
+ }
26
+
27
+ ENDPOINTS = {
28
+ submit_to_store: "order/apiorder".freeze,
29
+ submit_to_drop_ship: "order/dropship".freeze,
30
+ }
31
+
32
+ def initialize(client)
33
+ @client = client
50
34
  end
51
35
 
52
- private
36
+ def submit_to_store(order_data)
37
+ requires!(order_data, *SUBMIT_TO_STORE_ATTRS[:required])
53
38
 
54
- def build_order_data
55
- order_data = {
56
- EmailAddress: @email,
57
- Password: @password,
58
- LipseysItemNumberOrUPC: @item_number_or_upc,
59
- Qty: @quantity
60
- }
39
+ endpoint = ENDPOINTS[:submit_to_store]
40
+ headers = [
41
+ *auth_header(@client.access_token),
42
+ *content_type_header('application/json'),
43
+ ].to_h
61
44
 
62
- order_data[:NotifyByEmail] = @notify_by_email unless @notify_by_email.nil?
63
- order_data[:PONumber] = @purchase_order unless @purchase_order.nil?
64
- order_data[:Note] = @note unless @note.nil?
45
+ order_data = standardize_body_data(order_data, SUBMIT_TO_STORE_ATTRS[:permitted])
65
46
 
66
- { OrderData: order_data }
47
+ post_request(endpoint, order_data, headers)
67
48
  end
68
49
 
69
- def not_authenticated?(response)
70
- order_result = response.body[:submit_order_response][:submit_order_result]
71
- order_result[:success] == 'N' && order_result[:return_desc] =~ /Credentials Not Valid/i
50
+ def submit_to_drop_ship(order_data)
51
+ # NOTE: Will build this out as time permits.
52
+ false
72
53
  end
73
54
 
74
55
  end
@@ -0,0 +1,45 @@
1
+ module Lipseys
2
+ class Response
3
+
4
+ def initialize(response)
5
+ @response = response
6
+
7
+ case @response
8
+ when Net::HTTPUnauthorized
9
+ Lipseys::Error::NotAuthorized.new(@response.body)
10
+ when Net::HTTPNotFound
11
+ Lipseys::Error::NotFound.new(@response.body)
12
+ when Net::HTTPNoContent
13
+ Lipseys::Error::NoContent.new(@response.body)
14
+ when Net::HTTPOK, Net::HTTPSuccess
15
+ _data = (JSON.parse(@response.body) if @response.body.present?)
16
+
17
+ @data = case
18
+ when _data.is_a?(Hash)
19
+ _data.deep_symbolize_keys
20
+ when _data.is_a?(Array)
21
+ _data.map(&:deep_symbolize_keys)
22
+ end
23
+ else
24
+ raise Lipseys::Error::RequestError.new(@response.body)
25
+ end
26
+ end
27
+
28
+ def [](key)
29
+ @data[key]
30
+ end
31
+
32
+ def body
33
+ @data
34
+ end
35
+
36
+ def fetch(key)
37
+ @data.fetch(key)
38
+ end
39
+
40
+ def success?
41
+ !!self[:success] && self[:errors].empty?
42
+ end
43
+
44
+ end
45
+ end
@@ -1,29 +1,14 @@
1
1
  module Lipseys
2
- class User < SoapClient
3
-
4
- API_URL = 'http://184.188.80.195/API/validate.asmx?WSDL'
2
+ class User < Base
5
3
 
6
4
  def initialize(options = {})
7
5
  requires!(options, :username, :password)
8
- @options = options
9
- end
10
6
 
11
- def authenticated?
12
- validate[:success]
7
+ @client = Lipseys::Client.new(username: options[:username], password: options[:password])
13
8
  end
14
9
 
15
- def validate
16
- body = { Credentials: { EmailAddress: @options[:username], Password: @options[:password] } }
17
- response = soap_client(API_URL).call(:validate_dealer, message: body)
18
-
19
- result = response.body[:validate_dealer_response][:validate_dealer_result]
20
-
21
- {
22
- success: (result[:success] == 'Y'),
23
- description: result[:return_desc],
24
- }
25
- rescue Savon::Error => e
26
- { success: false, description: e.to_s }
10
+ def authenticated?
11
+ @client.access_token.present?
27
12
  end
28
13
 
29
14
  end
@@ -1,3 +1,3 @@
1
1
  module Lipseys
2
- VERSION = '5.0.1'.freeze
2
+ VERSION = '6.0.0'.freeze
3
3
  end
@@ -6,8 +6,8 @@ require 'lipseys/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "lipseys"
8
8
  spec.version = Lipseys::VERSION
9
- spec.authors = ["Dale Campbell"]
10
- spec.email = ["oshuma@gmail.com"]
9
+ spec.authors = ["Jeffrey Dill"]
10
+ spec.email = ["jeffdill2@gmail.com"]
11
11
 
12
12
  spec.summary = %q{Ruby library for Lipsey's API.}
13
13
  spec.description = %q{}
@@ -17,16 +17,14 @@ Gem::Specification.new do |spec|
17
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
18
  f.match(%r{^(test|spec|features)/})
19
19
  end
20
+
20
21
  spec.bindir = "exe"
21
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
23
  spec.require_paths = ["lib"]
23
24
 
24
- spec.add_dependency "nokogiri", "~> 1.6"
25
- spec.add_dependency "savon", "~> 2.11.1"
26
-
27
25
  spec.add_development_dependency "activesupport", "~> 5"
28
26
  spec.add_development_dependency "bundler", "~> 1.12"
29
- spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rake", ">= 12.3.3"
30
28
  spec.add_development_dependency "rspec", "~> 3.0"
31
29
  spec.add_development_dependency "webmock", "~> 2.3"
32
30
  end
metadata CHANGED
@@ -1,43 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lipseys
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.1
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
- - Dale Campbell
7
+ - Jeffrey Dill
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-16 00:00:00.000000000 Z
11
+ date: 2020-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: nokogiri
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.6'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.6'
27
- - !ruby/object:Gem::Dependency
28
- name: savon
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 2.11.1
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 2.11.1
41
13
  - !ruby/object:Gem::Dependency
42
14
  name: activesupport
43
15
  requirement: !ruby/object:Gem::Requirement
@@ -70,16 +42,16 @@ dependencies:
70
42
  name: rake
71
43
  requirement: !ruby/object:Gem::Requirement
72
44
  requirements:
73
- - - "~>"
45
+ - - ">="
74
46
  - !ruby/object:Gem::Version
75
- version: '10.0'
47
+ version: 12.3.3
76
48
  type: :development
77
49
  prerelease: false
78
50
  version_requirements: !ruby/object:Gem::Requirement
79
51
  requirements:
80
- - - "~>"
52
+ - - ">="
81
53
  - !ruby/object:Gem::Version
82
- version: '10.0'
54
+ version: 12.3.3
83
55
  - !ruby/object:Gem::Dependency
84
56
  name: rspec
85
57
  requirement: !ruby/object:Gem::Requirement
@@ -110,7 +82,7 @@ dependencies:
110
82
  version: '2.3'
111
83
  description: ''
112
84
  email:
113
- - oshuma@gmail.com
85
+ - jeffdill2@gmail.com
114
86
  executables: []
115
87
  extensions: []
116
88
  extra_rdoc_files: []
@@ -127,14 +99,16 @@ files:
127
99
  - bin/console
128
100
  - bin/setup
129
101
  - lib/lipseys.rb
102
+ - lib/lipseys/api.rb
130
103
  - lib/lipseys/base.rb
131
104
  - lib/lipseys/catalog.rb
105
+ - lib/lipseys/client.rb
106
+ - lib/lipseys/error.rb
132
107
  - lib/lipseys/image.rb
133
108
  - lib/lipseys/inventory.rb
134
- - lib/lipseys/invoice.rb
109
+ - lib/lipseys/items.rb
135
110
  - lib/lipseys/order.rb
136
- - lib/lipseys/parser.rb
137
- - lib/lipseys/soap_client.rb
111
+ - lib/lipseys/response.rb
138
112
  - lib/lipseys/user.rb
139
113
  - lib/lipseys/version.rb
140
114
  - lipseys.gemspec
@@ -158,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
132
  version: '0'
159
133
  requirements: []
160
134
  rubyforge_project:
161
- rubygems_version: 2.6.14
135
+ rubygems_version: 2.6.14.1
162
136
  signing_key:
163
137
  specification_version: 4
164
138
  summary: Ruby library for Lipsey's API.
@@ -1,83 +0,0 @@
1
- module Lipseys
2
- # In addition to the `:email` and `:password` options, finding invoices requires
3
- # either an `:order_number` **OR** a `:purchase_order`.
4
- #
5
- # The response structure will look like this:
6
- #
7
- # {
8
- # invoices_found: 1,
9
- # description: "1 Invoice(s) found.",
10
- # invoices: {
11
- # invoice_detail: {
12
- # invoice_no: "...",
13
- # ship_to_location: "...",
14
- # tracking_no: "...",
15
- # line_items: {
16
- # item_detail: {
17
- # item_no: "...",
18
- # upc: "...",
19
- # qty: "...",
20
- # serialized_item: "...",
21
- # serial_numbers: {
22
- # string: "..."
23
- # }
24
- # }
25
- # }
26
- # }
27
- # }
28
- # }
29
- class Invoice < SoapClient
30
-
31
- def initialize(options = {})
32
- requires!(options, :username, :password)
33
- @email = options[:username]
34
- @password = options[:password]
35
-
36
- @order_number = options[:order_number]
37
- @purchase_order = options[:purchase_order]
38
- raise ArgumentError.new("Either :order_number or :purchase_order is required") if @order_number.nil? && @purchase_order.nil?
39
- end
40
-
41
-
42
- def self.all(*args)
43
- new(*args).all
44
- end
45
-
46
- def all
47
- response = soap_client.call(:get_invoices, message: build_inquiry_data)
48
-
49
- raise Lipseys::NotAuthenticated if not_authenticated?(response)
50
-
51
- invoice_result = response.body[:get_invoices_response][:get_invoices_result]
52
-
53
- {
54
- invoices_found: Integer(invoice_result[:invoices_found]),
55
- description: invoice_result[:return_desc],
56
-
57
- invoices: invoice_result[:invoices]
58
- }
59
- rescue Savon::Error => e
60
- { success: false, description: e.to_s }
61
- end
62
-
63
- private
64
-
65
- def build_inquiry_data
66
- inquiry_data = {
67
- EmailAddress: @email,
68
- Password: @password
69
- }
70
-
71
- inquiry_data[:OrderNo] = @order_number unless @order_number.nil?
72
- inquiry_data[:PONumber] = @purchase_order unless @purchase_order.nil?
73
-
74
- { InquiryData: inquiry_data }
75
- end
76
-
77
- def not_authenticated?(response)
78
- invoice_result = response.body[:get_invoices_response][:get_invoices_result]
79
- invoice_result[:return_desc] =~ /Credentials Not Valid/i
80
- end
81
-
82
- end
83
- end
@@ -1,25 +0,0 @@
1
- module Lipseys
2
- class Parser
3
-
4
- attr_reader :file
5
-
6
- def initialize(file)
7
- @file = file
8
- end
9
-
10
- def self.parse(file, node_name, &block)
11
- new(file).parse(node_name, &block)
12
- end
13
-
14
- def parse(node_name, &block)
15
- File.open(@file) do |file|
16
- Nokogiri::XML::Reader.from_io(file).each do |node|
17
- if node.name == node_name and node.node_type == Nokogiri::XML::Reader::TYPE_ELEMENT
18
- yield(Nokogiri::XML(node.outer_xml))
19
- end
20
- end
21
- end
22
- end
23
-
24
- end
25
- end
@@ -1,13 +0,0 @@
1
- module Lipseys
2
- class SoapClient < Base
3
-
4
- API_URL = 'http://184.188.80.195/webservice/LipseyAPI.asmx?WSDL'
5
-
6
- protected
7
-
8
- def soap_client(api_url = API_URL)
9
- @soap_client ||= Savon.client(wsdl: api_url, convert_request_keys_to: :none)
10
- end
11
-
12
- end
13
- end