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 +4 -4
- data/lib/lipseys.rb +29 -11
- data/lib/lipseys/api.rb +121 -0
- data/lib/lipseys/base.rb +4 -50
- data/lib/lipseys/catalog.rb +41 -64
- data/lib/lipseys/client.rb +40 -0
- data/lib/lipseys/error.rb +11 -0
- data/lib/lipseys/inventory.rb +8 -40
- data/lib/lipseys/items.rb +54 -0
- data/lib/lipseys/order.rb +44 -63
- data/lib/lipseys/response.rb +45 -0
- data/lib/lipseys/user.rb +4 -19
- data/lib/lipseys/version.rb +1 -1
- data/lipseys.gemspec +4 -6
- metadata +14 -40
- data/lib/lipseys/invoice.rb +0 -83
- data/lib/lipseys/parser.rb +0 -25
- data/lib/lipseys/soap_client.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4403d00b0a7ca5e2b6754e739d85bae695e8c6ce
|
4
|
+
data.tar.gz: c8e997c51bc28411ea030341b77591a20e6f9b58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5c1ad6a782c66656e2dafe7f4dedc0fecf2232e0c5425319d7b5ac0a775633ab4a9dcb4f9f7461a29407e713492e3c977fdf58ffabc4f46ad9649e02dbf2dd7
|
7
|
+
data.tar.gz: 5589846a2cbbee7b70eaa57831b42d385a09ba42950d1205162c42bb033070abadcb880b4d96daaf3821cb47154abe9c45c131ddc2fff2a30643774d2b5173fa
|
data/lib/lipseys.rb
CHANGED
@@ -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/
|
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/
|
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
|
-
|
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
|
data/lib/lipseys/api.rb
ADDED
@@ -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
|
data/lib/lipseys/base.rb
CHANGED
@@ -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
|
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
|
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
|
data/lib/lipseys/catalog.rb
CHANGED
@@ -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
|
-
@
|
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
|
-
|
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(
|
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:
|
63
|
-
model: model,
|
64
|
-
upc:
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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:
|
74
|
-
action:
|
75
|
-
barrel:
|
76
|
-
capacity:
|
77
|
-
finish:
|
78
|
-
length:
|
79
|
-
receiver:
|
80
|
-
safety:
|
81
|
-
sights:
|
82
|
-
|
83
|
-
|
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
|
data/lib/lipseys/inventory.rb
CHANGED
@@ -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
|
-
@
|
7
|
+
@client = Lipseys::Client.new(username: options[:username], password: options[:password])
|
10
8
|
end
|
11
9
|
|
12
10
|
def self.all(options = {})
|
13
|
-
|
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(
|
23
|
+
def map_hash(item)
|
56
24
|
{
|
57
|
-
item_identifier:
|
58
|
-
quantity:
|
59
|
-
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
|
data/lib/lipseys/order.rb
CHANGED
@@ -1,74 +1,55 @@
|
|
1
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
36
|
+
def submit_to_store(order_data)
|
37
|
+
requires!(order_data, *SUBMIT_TO_STORE_ATTRS[:required])
|
53
38
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
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
|
-
|
47
|
+
post_request(endpoint, order_data, headers)
|
67
48
|
end
|
68
49
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
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
|
data/lib/lipseys/user.rb
CHANGED
@@ -1,29 +1,14 @@
|
|
1
1
|
module Lipseys
|
2
|
-
class User <
|
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
|
-
|
12
|
-
validate[:success]
|
7
|
+
@client = Lipseys::Client.new(username: options[:username], password: options[:password])
|
13
8
|
end
|
14
9
|
|
15
|
-
def
|
16
|
-
|
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
|
data/lib/lipseys/version.rb
CHANGED
data/lipseys.gemspec
CHANGED
@@ -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 = ["
|
10
|
-
spec.email = ["
|
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", "
|
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:
|
4
|
+
version: 6.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Jeffrey Dill
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
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:
|
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:
|
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
|
-
-
|
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/
|
109
|
+
- lib/lipseys/items.rb
|
135
110
|
- lib/lipseys/order.rb
|
136
|
-
- lib/lipseys/
|
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.
|
data/lib/lipseys/invoice.rb
DELETED
@@ -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
|
data/lib/lipseys/parser.rb
DELETED
@@ -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
|
data/lib/lipseys/soap_client.rb
DELETED
@@ -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
|