fastbound-ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bf0dfc7f844190045d5c4014a46a84d5fa898d614e10ad15b919110a997221e5
4
+ data.tar.gz: 1f875a1df0916ef269723b58ed3e7bcb3d3bb4a7f4cafa57850d625d637b9b12
5
+ SHA512:
6
+ metadata.gz: c003c356e3681e451a0e26b26d0a0b3326ad831c25227632dda3f7c22c29a60b9f828c2463982be6174560bcce69748ca472718ccc6a8587e6cf9d390f7c643a
7
+ data.tar.gz: 6863e30394b7bf0f5289c1309ade62f25be1accd51f427472173dc319cde3dbeadbca43d01253867e64f0f94f6acf8b9ffc70c866c4b61d68d82e7763f60be47
data/.gitignore ADDED
@@ -0,0 +1,67 @@
1
+ *.rbc
2
+ capybara-*.html
3
+ .rspec
4
+ /db/*.sqlite3
5
+ /db/*.sqlite3-journal
6
+ /db/*.sqlite3-[0-9]*
7
+ /public/system
8
+ /coverage/
9
+ /spec/tmp
10
+ *.orig
11
+ rerun.txt
12
+ pickle-email-*.html
13
+
14
+ # Ignore all logfiles and tempfiles.
15
+ /log/*
16
+ /tmp/*
17
+ !/log/.keep
18
+ !/tmp/.keep
19
+
20
+ # TODO Comment out this rule if you are OK with secrets being uploaded to the repo
21
+ config/initializers/secret_token.rb
22
+ config/master.key
23
+
24
+ # Only include if you have production secrets in this file, which is no longer a Rails default
25
+ # config/secrets.yml
26
+
27
+ # dotenv
28
+ # TODO Comment out this rule if environment variables can be committed
29
+ .env
30
+
31
+ ## Environment normalization:
32
+ /.bundle
33
+ /vendor/bundle
34
+
35
+ # these should all be checked in to normalize the environment:
36
+ # Gemfile.lock, .ruby-version, .ruby-gemset
37
+
38
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
39
+ .rvmrc
40
+
41
+ # if using bower-rails ignore default bower_components path bower.json files
42
+ /vendor/assets/bower_components
43
+ *.bowerrc
44
+ bower.json
45
+
46
+ # Ignore pow environment settings
47
+ .powenv
48
+
49
+ # Ignore Byebug command history file.
50
+ .byebug_history
51
+
52
+ # Ignore node_modules
53
+ node_modules/
54
+
55
+ # Ignore precompiled javascript packs
56
+ /public/packs
57
+ /public/packs-test
58
+ /public/assets
59
+
60
+ # Ignore yarn files
61
+ /yarn-error.log
62
+ yarn-debug.log*
63
+ .yarn-integrity
64
+
65
+ # Ignore uploaded files in development
66
+ /storage/*
67
+ !/storage/.keep
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fastbound-ruby.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ fastbound-ruby (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ addressable (2.8.0)
10
+ public_suffix (>= 2.0.2, < 5.0)
11
+ coderay (1.1.3)
12
+ crack (0.4.5)
13
+ rexml
14
+ diff-lcs (1.4.4)
15
+ hashdiff (1.0.1)
16
+ method_source (0.8.2)
17
+ pry (0.10.4)
18
+ coderay (~> 1.1.0)
19
+ method_source (~> 0.8.1)
20
+ slop (~> 3.4)
21
+ public_suffix (4.0.6)
22
+ rake (13.0.6)
23
+ rexml (3.2.5)
24
+ rspec (3.10.0)
25
+ rspec-core (~> 3.10.0)
26
+ rspec-expectations (~> 3.10.0)
27
+ rspec-mocks (~> 3.10.0)
28
+ rspec-core (3.10.1)
29
+ rspec-support (~> 3.10.0)
30
+ rspec-expectations (3.10.1)
31
+ diff-lcs (>= 1.2.0, < 2.0)
32
+ rspec-support (~> 3.10.0)
33
+ rspec-mocks (3.10.2)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.10.0)
36
+ rspec-support (3.10.2)
37
+ slop (3.6.0)
38
+ webmock (1.24.6)
39
+ addressable (>= 2.3.6)
40
+ crack (>= 0.3.2)
41
+ hashdiff
42
+ yard (0.9.26)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ bundler (~> 1.7)
49
+ fastbound-ruby!
50
+ pry (~> 0.10.4)
51
+ rake (>= 12.3.3)
52
+ rspec (~> 3.1)
53
+ webmock (~> 1.20)
54
+ yard (~> 0.9.12)
55
+
56
+ BUNDLED WITH
57
+ 1.17.3
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Jeffrey Dill
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # FastBound
2
+
3
+ FastBound API Ruby library.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'fastbound-ruby'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install fastbound-ruby
20
+
21
+ ## Usage
22
+
23
+ ### Documentation
24
+
25
+ The full documentation is located here: http://www.rubydoc.info/gems/fastbound-ruby
26
+
27
+ ## Contributing
28
+
29
+ 1. Fork it ( https://github.com/ammoready/fastbound-ruby/fork )
30
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
31
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
32
+ 4. Push to the branch (`git push origin my-new-feature`)
33
+ 5. Create a new Pull Request
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "fastbound-ruby"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require "pry"
10
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ bundle install
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fastbound-ruby/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "fastbound-ruby"
8
+ spec.version = FastBound::VERSION
9
+ spec.authors = ["Jeffrey Dill"]
10
+ spec.email = ["jeffdill2@gmail.com"]
11
+ spec.summary = %q{FastBound API Ruby library.}
12
+ spec.description = %q{}
13
+ spec.homepage = "https://github.com/ammoready/fastbound-ruby"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "pry", "~> 0.10.4"
22
+ spec.add_development_dependency "rake", ">= 12.3.3"
23
+ spec.add_development_dependency "yard", "~> 0.9.12"
24
+ end
@@ -0,0 +1,23 @@
1
+ require 'fastbound-ruby/api'
2
+
3
+ module FastBound
4
+ class Account < Base
5
+
6
+ include FastBound::API
7
+
8
+ ENDPOINTS = {
9
+ fetch: "account".freeze,
10
+ }
11
+
12
+ def initialize(client)
13
+ @client = client
14
+ end
15
+
16
+ def fetch
17
+ endpoint = ENDPOINTS[:fetch]
18
+
19
+ get_request(@client, endpoint)
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,134 @@
1
+ require 'fastbound-ruby/api'
2
+
3
+ module FastBound
4
+ class Acquisition < Base
5
+
6
+ include FastBound::API
7
+
8
+ CREATE_AND_EDIT_ATTRS = {
9
+ permitted: %i(
10
+ external_id is_manufacturing_acquisition purchase_order_number
11
+ date type note invoice_number shipment_tracking_number
12
+ ).freeze,
13
+ required: %i( type ).freeze
14
+ }
15
+
16
+ ENDPOINTS = {
17
+ list: "acquisitions".freeze,
18
+ create: "acquisitions".freeze,
19
+ edit: "acquisitions/%s".freeze,
20
+ fetch: "acquisitions/%s".freeze,
21
+ fetch_by_external_id: "acquisitions/getbyexternalid/%s".freeze,
22
+ delete: "acquisitions/%s".freeze,
23
+ commit: "acquisitions/%s/commit".freeze,
24
+ create_and_commit: "acquisitions/createandcommit".freeze,
25
+ create_item: "acquisitions/%s/items".freeze,
26
+ edit_item: "acquisitions/%s/items/%s".freeze,
27
+ fetch_item: "acquisitions/%s/items/%s".freeze,
28
+ delete_item: "acquisitions/%s/items/%s".freeze,
29
+ attach_contact: "acquisitions/%s/attachcontact/%s".freeze
30
+ }
31
+
32
+ def initialize(client)
33
+ @client = client
34
+ end
35
+
36
+ def list(params = {})
37
+ endpoint = ENDPOINTS[:list] % convert_params_to_request_query(params)
38
+
39
+ get_request(@client, endpoint)
40
+ end
41
+
42
+ def create(acquisition_data)
43
+ requires!(acquisition_data, CREATE_AND_EDIT_ATTRS[:required])
44
+
45
+ endpoint = ENDPOINTS[:create]
46
+ acquisition_data = standardize_body_data(acquisition_data, CREATE_AND_EDIT_ATTRS[:permitted])
47
+
48
+ post_request(@client, endpoint, acquisition_data)
49
+ end
50
+
51
+ def edit(acquisition_id, acquisition_data)
52
+ endpoint = ENDPOINTS[:edit] % acquisition_id
53
+ acquisition_data = standardize_body_data(acquisition_data, CREATE_AND_EDIT_ATTRS[:permitted])
54
+
55
+ put_request(@client, endpoint, acquisition_data)
56
+ end
57
+
58
+ def fetch(acquisition_id)
59
+ endpoint = ENDPOINTS[:fetch] % acquisition_id
60
+
61
+ get_request(@client, endpoint)
62
+ end
63
+
64
+ def fetch_by_external_id(external_id)
65
+ endpoint = ENDPOINTS[:fetch_by_external_id] % external_id
66
+
67
+ get_request(@client, endpoint)
68
+ end
69
+
70
+ def delete(acquisition_id)
71
+ endpoint = ENDPOINTS[:delete] % acquisition_id
72
+
73
+ delete_request(@client, endpoint)
74
+ end
75
+
76
+ def commit(acquisition_id)
77
+ endpoint = ENDPOINTS[:commit] % acquisition_id
78
+
79
+ post_request(@client, endpoint)
80
+ end
81
+
82
+ def create_and_commit(acquisition_data, item_data, contact_data)
83
+ requires!(acquisition_data, CREATE_AND_EDIT_ATTRS[:required])
84
+ requires!(item_data, Item::CREATE_AND_EDIT_ATTRS[:required])
85
+ requires!(contact_data, Contact::CREATE_AND_EDIT_ATTRS[:required])
86
+
87
+ endpoint = ENDPOINTS[:create_and_commit]
88
+ acquisition_data = standardize_body_data(acquisition_data, CREATE_AND_EDIT_ATTRS[:permitted])
89
+ item_data = standardize_body_data(item_data, Item::CREATE_AND_EDIT_ATTRS[:permitted])
90
+ contact_data = standardize_body_data(contact_data, Contact::CREATE_AND_EDIT_ATTRS[:permitted])
91
+ request_data = acquisition_data.merge(
92
+ contact: contact_data,
93
+ items: [item_data]
94
+ )
95
+
96
+ post_request(@client, endpoint, request_data)
97
+ end
98
+
99
+ def create_item(acquisition_id, item_data)
100
+ requires!(item_data, Item::CREATE_AND_EDIT_ATTRS[:required])
101
+
102
+ endpoint = ENDPOINTS[:create_item] % acquisition_id
103
+ item_data = standardize_body_data(item_data, Item::CREATE_AND_EDIT_ATTRS[:permitted])
104
+
105
+ post_request(@client, endpoint, item_data)
106
+ end
107
+
108
+ def edit_item(acquisition_id, item_id, item_data)
109
+ endpoint = ENDPOINTS[:edit_item] % [acquisition_id, item_id]
110
+ item_data = standardize_body_data(item_data, Item::CREATE_AND_EDIT_ATTRS[:permitted])
111
+
112
+ put_request(@client, endpoint, item_data)
113
+ end
114
+
115
+ def fetch_item(acquisition_id, item_id)
116
+ endpoint = ENDPOINTS[:fetch_item] % [acquisition_id, item_id]
117
+
118
+ get_request(@client, endpoint)
119
+ end
120
+
121
+ def delete_item(acquisition_id, item_id)
122
+ endpoint = ENDPOINTS[:delete_item] % [acquisition_id, item_id]
123
+
124
+ delete_request(@client, endpoint)
125
+ end
126
+
127
+ def attach_contact(acquisition_id, contact_id)
128
+ endpoint = ENDPOINTS[:attach_contact] % [acquisition_id, contact_id]
129
+
130
+ put_request(@client, endpoint)
131
+ end
132
+
133
+ end
134
+ end
@@ -0,0 +1,121 @@
1
+ require 'net/http'
2
+
3
+ module FastBound
4
+ module API
5
+
6
+ ROOT_URL = 'https://cloud.fastbound.com'.freeze
7
+ USER_AGENT = "FastBoundRubyGem/#{FastBound::VERSION}".freeze
8
+ FILE_UPLOAD_ATTRS = {
9
+ permitted: %i( file_name file_type file_contents ).freeze,
10
+ reqired: %i( file_type file_contents ).freeze,
11
+ }
12
+
13
+ def get_request(client, endpoint)
14
+ request = Net::HTTP::Get.new(request_url(client, endpoint))
15
+
16
+ submit_request(client, request)
17
+ end
18
+
19
+ def post_request(client, endpoint, data = {})
20
+ request = Net::HTTP::Post.new(request_url(client, endpoint))
21
+
22
+ submit_request(client, request, data)
23
+ end
24
+
25
+ def put_request(client, endpoint, data = {})
26
+ request = Net::HTTP::Put.new(request_url(client, endpoint))
27
+
28
+ submit_request(client, request, data)
29
+ end
30
+
31
+ def delete_request(client, endpoint)
32
+ request = Net::HTTP::Delete.new(request_url(client, endpoint))
33
+
34
+ submit_request(client, request)
35
+ end
36
+
37
+ def post_file_request(client, endpoint, file_data)
38
+ request = Net::HTTP::Post.new(request_url(client, endpoint))
39
+
40
+ submit_file_request(client, request, file_data)
41
+ end
42
+
43
+ private
44
+
45
+ def submit_request(client, request, data = {})
46
+ set_request_headers(client, request)
47
+
48
+ request.body = data.is_a?(Hash) ? data.to_json : data
49
+
50
+ process_request(request)
51
+ end
52
+
53
+ def submit_file_request(client, request, file_data)
54
+ boundary = ::SecureRandom.hex(15)
55
+
56
+ headers.merge!(content_type_header("multipart/form-data; boundary=#{boundary}"))
57
+
58
+ build_multipart_request_body(request, file_data, boundary)
59
+ set_request_headers(client, request)
60
+ process_request(request)
61
+ end
62
+
63
+ def process_request(request)
64
+ uri = URI(request.path)
65
+
66
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
67
+ http.request(request)
68
+ end
69
+
70
+ FastBound::Response.new(response)
71
+ end
72
+
73
+ def build_multipart_request_body(request, file_data, boundary)
74
+ file_type = file_data[:file_type]
75
+ file_contents = file_data[:file_contents]
76
+ file_name = file_data[:file_name] || "ffl-document.#{file_type}"
77
+ content_type = "application/#{file_data[:file_type]}"
78
+
79
+ body = []
80
+ body << "--#{boundary}\r\n"
81
+ body << "Content-Disposition: form-data; name=\"file\"; filename=\"#{file_name}\"\r\n"
82
+ body << "Content-Type: #{content_type}\r\n"
83
+ body << "\r\n"
84
+ body << "#{file_contents}\r\n"
85
+ body << "--#{boundary}--\r\n"
86
+
87
+ request.body = body.join
88
+ end
89
+
90
+ def set_request_headers(client, request)
91
+ request['User-Agent'] = USER_AGENT
92
+ request['Authorization'] = ['Basic', Base64.strict_encode64(client.api_key + ':')].join(' ')
93
+ request['X-AuditUser'] = client.account_email
94
+ end
95
+
96
+ def request_url(client, endpoint)
97
+ [ROOT_URL, client.account_number, 'api', endpoint].join('/')
98
+ end
99
+
100
+ def standardize_body_data(submitted_data, permitted_data_attrs)
101
+ protected_attr_prefixes = %w( acquire destroyed dispose location theftLoss )
102
+ _submitted_data = submitted_data.deep_transform_keys(&:to_sym)
103
+ permitted_data = (_submitted_data.select! { |k, v| permitted_data_attrs.include?(k) } || _submitted_data)
104
+
105
+ permitted_data.deep_transform_keys do |k|
106
+ transformed_key = k.to_s.camelize(:lower)
107
+
108
+ if index_for_underscore = protected_attr_prefixes.find { |x| transformed_key.index(x) == 0 }&.length
109
+ transformed_key.insert(index_for_underscore, '_')
110
+ else
111
+ transformed_key
112
+ end
113
+ end
114
+ end
115
+
116
+ def convert_params_to_request_query(params)
117
+ params&.map { |k, v| [k.to_s.camelize(:lower), v].join('=') }&.join('&')
118
+ end
119
+
120
+ end
121
+ end
@@ -0,0 +1,23 @@
1
+ require 'fastbound-ruby/api'
2
+
3
+ module FastBound
4
+ class Attachment < Base
5
+
6
+ include FastBound::API
7
+
8
+ ENDPOINTS = {
9
+ download: "attachments/download/%s".freeze,
10
+ }
11
+
12
+ def initialize(client)
13
+ @client = client
14
+ end
15
+
16
+ def download(attachment_id)
17
+ endpoint = ENDPOINTS[:download] % attachment_id
18
+
19
+ get_request(@client, endpoint)
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ module FastBound
2
+ class Base
3
+
4
+ protected
5
+
6
+ def requires!(*args)
7
+ self.class.requires!(*args)
8
+ end
9
+
10
+ def self.requires!(hash, *params)
11
+ params.each do |param|
12
+ if param.is_a?(Array)
13
+ raise ArgumentError.new("Missing required parameter: #{param.first}") unless hash.has_key?(param.first)
14
+
15
+ valid_options = param[1..-1]
16
+ raise ArgumentError.new("Parameter: #{param.first} must be one of: #{valid_options.join(', ')}") unless valid_options.include?(hash[param.first])
17
+ else
18
+ raise ArgumentError.new("Missing required parameter: #{param}") unless hash.has_key?(param)
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,59 @@
1
+ require 'fastbound-ruby/api'
2
+ require 'fastbound-ruby/account'
3
+ require 'fastbound-ruby/acquisition'
4
+ require 'fastbound-ruby/attachment'
5
+ require 'fastbound-ruby/contact'
6
+ require 'fastbound-ruby/disposition'
7
+ require 'fastbound-ruby/item'
8
+ require 'fastbound-ruby/smart_list'
9
+ require 'fastbound-ruby/webhook'
10
+
11
+ module FastBound
12
+ class Client < Base
13
+
14
+ include FastBound::API
15
+
16
+ attr_accessor :account_number, :api_key, :account_email
17
+
18
+ def initialize(options = {})
19
+ requires!(options, :account_number, :api_key, :account_email)
20
+
21
+ self.account_number = options[:account_number]
22
+ self.api_key = options[:api_key]
23
+ self.account_email = options[:account_email]
24
+ end
25
+
26
+ def account
27
+ @account ||= FastBound::Account.new(self)
28
+ end
29
+
30
+ def acquisition
31
+ @acquisition ||= FastBound::Acquisition.new(self)
32
+ end
33
+
34
+ def attachment
35
+ @attachment ||= FastBound::Attachment.new(self)
36
+ end
37
+
38
+ def contact
39
+ @contact ||= FastBound::Contact.new(self)
40
+ end
41
+
42
+ def disposition
43
+ @disposition ||= FastBound::Disposition.new(self)
44
+ end
45
+
46
+ def item
47
+ @item ||= FastBound::Item.new(self)
48
+ end
49
+
50
+ def smart_list
51
+ @smart_list ||= FastBound::SmartList.new(self)
52
+ end
53
+
54
+ def webhook
55
+ @webhook ||= FastBound::Webhook.new(self)
56
+ end
57
+
58
+ end
59
+ end