infuser 1.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 +7 -0
- data/.gitignore +6 -0
- data/README.md +242 -0
- data/Rakefile +8 -0
- data/infuser.gemspec +22 -0
- data/lib/infuser.rb +49 -0
- data/lib/infuser/address.rb +38 -0
- data/lib/infuser/client.rb +32 -0
- data/lib/infuser/collections/base.rb +70 -0
- data/lib/infuser/collections/child_proxy.rb +57 -0
- data/lib/infuser/collections/proxy.rb +76 -0
- data/lib/infuser/company.rb +12 -0
- data/lib/infuser/configuration.rb +35 -0
- data/lib/infuser/contact.rb +37 -0
- data/lib/infuser/email.rb +11 -0
- data/lib/infuser/exceptions.rb +57 -0
- data/lib/infuser/fax.rb +12 -0
- data/lib/infuser/helpers/hashie.rb +17 -0
- data/lib/infuser/invoice.rb +21 -0
- data/lib/infuser/invoice_item.rb +11 -0
- data/lib/infuser/logger.rb +32 -0
- data/lib/infuser/models/base.rb +205 -0
- data/lib/infuser/order_item.rb +10 -0
- data/lib/infuser/phone.rb +13 -0
- data/lib/infuser/requester.rb +80 -0
- data/lib/infuser/tables/base.rb +77 -0
- data/lib/infuser/tables/company.rb +6 -0
- data/lib/infuser/tables/contact.rb +6 -0
- data/lib/infuser/tables/invoice.rb +20 -0
- data/lib/infuser/tables/invoice_item.rb +6 -0
- data/lib/infuser/tables/order_item.rb +6 -0
- data/lib/infuser/token_refresher.rb +35 -0
- data/lib/infuser/version.rb +5 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ced6e7246187597ea92df1ff1d847afe130377dc
|
4
|
+
data.tar.gz: 64dfee1e7ebfbc4707cb3a0b249a54d548a8299d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ac008b42df54932f0a6a21327e2f492bf397bd09d44db310febfc4617107d96253d0f5bc7fc178bb306b8149e99473e02d9181211fcce6ce2a53aad3c5756ca4
|
7
|
+
data.tar.gz: 336aced5a96227541377156f7b6b8de36a63c8d675cccc148c793b762a7aed04a4f9b3d3f3e9f6639204f7e37b2988aa6b9ed372275a165c8f721d35d283581f
|
data/.gitignore
ADDED
data/README.md
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
# Infuser
|
2
|
+
|
3
|
+
A cleaner Ruby wrapper for the InfusionSoft API.
|
4
|
+
|
5
|
+
This gem is for use with the Infusionsoft OAuth2 API. If you are using the token-based API, see [Nathan Levitt's gem](https://github.com/nateleavitt/infusionsoft), which this gem is based on.
|
6
|
+
|
7
|
+
## Philosophy
|
8
|
+
|
9
|
+
Infusionsoft has quite the annoying API. One example: field names are not standardized. Another: some API calls require specific arguments in a specific order, others take a hash.
|
10
|
+
|
11
|
+
I've gone out of my way to standardize these things behind the scenes in order to provide a cleaner, Ruby-esque way of dealing with the API.
|
12
|
+
|
13
|
+
## Caveats
|
14
|
+
|
15
|
+
I developed this gem as part of a project for a client. It covers companies, contacts, and invoices, but not much else, as that was all the client required. Pull requests are always welcome.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
1. Add the gem to your gemfile.
|
19
|
+
|
20
|
+
```
|
21
|
+
gem install infuser
|
22
|
+
```
|
23
|
+
|
24
|
+
2. Create an initializer file in your Rails app, with your Infusionsoft API settings:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
Infuser::Configuration.configure do |config|
|
28
|
+
config.api_key = 'Your-App-API-Key'
|
29
|
+
config.api_secret = 'Your-App-API-Secret'
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
Within the configuration block above, you can also set the `logger` as well as `retry_count` for how many times a call should be attempted before giving up.
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
This gem is for use with Infusionsoft's *OAuth2* API, not the token-based API.
|
38
|
+
|
39
|
+
Use the [omniauth-infusionsoft](https://github.com/l1h3r/omniauth-infusionsoft) gem to allow your users to connect and authorize their accounts, just like you would connect [any other OAuth gem](http://railscasts.com/episodes/360-facebook-authentication).
|
40
|
+
|
41
|
+
Once a user authorizes their account, Infusionsoft returns an `access_token`. You use this `access_token` to begin using this Infuser gem.
|
42
|
+
|
43
|
+
#### General Structure
|
44
|
+
|
45
|
+
All models work the same way, and are as close to ActiveRecord as possible. Here is an example with Contacts.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
client = Infuser::Client.new(access-token)
|
49
|
+
|
50
|
+
# retrieve contacts
|
51
|
+
client.contacts.all
|
52
|
+
|
53
|
+
# find a contact
|
54
|
+
client.contacts.find(1)
|
55
|
+
|
56
|
+
# search for a contact
|
57
|
+
client.contacts.find_by(first_name: 'David', last_name: 'Lesches')
|
58
|
+
|
59
|
+
# initialize a contact without saving it
|
60
|
+
client.contacts.build(first_name: 'David', last_name: 'Lesches')
|
61
|
+
|
62
|
+
# create a new contact
|
63
|
+
client.contacts.create(first_name: 'David', last_name: 'Lesches')
|
64
|
+
|
65
|
+
# update a contact
|
66
|
+
contact = client.contacts.find(1)
|
67
|
+
contact.first_name = 'John'
|
68
|
+
contact.save
|
69
|
+
|
70
|
+
# get a contact's attributes as a hash
|
71
|
+
contact = client.contacts.find(1)
|
72
|
+
contact.attributes
|
73
|
+
|
74
|
+
# delete a contact
|
75
|
+
contact = client.contacts.find(1)
|
76
|
+
contact.destroy
|
77
|
+
```
|
78
|
+
|
79
|
+
#### Contacts
|
80
|
+
|
81
|
+
Complete field list: `:first_name, :middle_name, :nickname, :last_name, :suffix, :title, :company_id, :job_title, :assistant_name, :assistant_phone, :contact_notes, :contact_type, :referral_code, :spouse_name, :username, :website, :date_created, :last_updated`
|
82
|
+
|
83
|
+
A contact can also have many phones, faxes, emails, and addresses. A contact can belong to a company.
|
84
|
+
|
85
|
+
#### Companies
|
86
|
+
|
87
|
+
Complete field list: `:company, :website, :date_created, :last_updated`. Note that the "name" field for a company is called 'company'. Infusionsoft :)
|
88
|
+
|
89
|
+
A contact can also have many phones, faxes, emails, and addresses.
|
90
|
+
|
91
|
+
You can also assign a contact to a company:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
client = Infuser::Client.new(access-token)
|
95
|
+
company = client.companies.find(1)
|
96
|
+
contact = client.contacts.find(1)
|
97
|
+
contact.company = company # => assigns and saves company 1 to contact
|
98
|
+
contact.company # => retrieves company 1
|
99
|
+
|
100
|
+
company.contacts # => get all contacts for this company
|
101
|
+
```
|
102
|
+
|
103
|
+
#### Addresses
|
104
|
+
|
105
|
+
Both Companies and Contacts have many addresses.
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
# See all addresses for this contact
|
109
|
+
contact.addresses
|
110
|
+
|
111
|
+
# Add an address to this contact
|
112
|
+
contact.addresses << Infuser::Address.new(street_address: '123 Broadway')
|
113
|
+
contact.save
|
114
|
+
|
115
|
+
# Remove an address from this contact
|
116
|
+
address = contact.addresses.find(1)
|
117
|
+
contact.addresses.remove(address)
|
118
|
+
contact.save
|
119
|
+
```
|
120
|
+
|
121
|
+
Complete field list: `:street_address, :street_address2, :address_type, :city, :state, :country, :postal_code, :zip`
|
122
|
+
|
123
|
+
#### Phones
|
124
|
+
|
125
|
+
Both Companies and Contacts have many phones.
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
# See all phones for this contact
|
129
|
+
contact.phones
|
130
|
+
|
131
|
+
# Add a phone to this contact
|
132
|
+
contact.phones << Infuser::Phone.new(number: '1-222-333-4444')
|
133
|
+
contact.save
|
134
|
+
|
135
|
+
# Remove a phone from this contact
|
136
|
+
phone = contact.phones.find(1)
|
137
|
+
contact.phones.remove(phone)
|
138
|
+
contact.save
|
139
|
+
```
|
140
|
+
|
141
|
+
Complete field list: `:number, :extension, :type`
|
142
|
+
|
143
|
+
#### Faxes
|
144
|
+
|
145
|
+
Both Companies and Contacts have many faxes.
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
# See all faxes for this contact
|
149
|
+
contact.faxes
|
150
|
+
|
151
|
+
# Add a fax to this contact
|
152
|
+
contact.faxes << Infuser::Fax.new(number: '1-222-333-4444')
|
153
|
+
contact.save
|
154
|
+
|
155
|
+
# Remove a fax from this contact
|
156
|
+
fax = contact.faxes.find(1)
|
157
|
+
contact.faxes.remove(fax)
|
158
|
+
contact.save
|
159
|
+
```
|
160
|
+
|
161
|
+
Complete field list: `:number, :extension, :type`
|
162
|
+
|
163
|
+
#### Emails
|
164
|
+
|
165
|
+
Both Companies and Contacts have many emails.
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
# See all emails for this contact
|
169
|
+
contact.emails
|
170
|
+
|
171
|
+
# Add an email to this contact
|
172
|
+
contact.emails << Infuser::Email.new(email: 'info@google.com')
|
173
|
+
contact.save
|
174
|
+
|
175
|
+
# Remove an email from this contact
|
176
|
+
email = contact.emails.find(1)
|
177
|
+
contact.emails.remove(email)
|
178
|
+
contact.save
|
179
|
+
```
|
180
|
+
|
181
|
+
Complete field list: `:email`
|
182
|
+
|
183
|
+
#### Invoices
|
184
|
+
|
185
|
+
Because Infusionsoft is awesome, creating invoices requires specific parameters in exact order.
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
# 1: contact_id
|
189
|
+
# 2: description
|
190
|
+
# 3: date
|
191
|
+
# 4: lead_affiliate_id (0 should be used if none)
|
192
|
+
# 5: sale_affiliate_id (0 should be used if none)
|
193
|
+
|
194
|
+
client.invoices.create(1, "A test invoice", Time.zone.now.to_date, 0, 0)
|
195
|
+
```
|
196
|
+
|
197
|
+
Once an invoice is created, you can add items to it. These too require specific parameters in exact order.
|
198
|
+
```ruby
|
199
|
+
# 1: product_id
|
200
|
+
# 2: type (UNKNOWN = 0, SHIPPING = 1, TAX = 2, SERVICE = 3, PRODUCT = 4, UPSELL = 5, FINANCECHARGE = 6, SPECIAL = 7)
|
201
|
+
# 3: price
|
202
|
+
# 4: quantity
|
203
|
+
# 5: description
|
204
|
+
# 6: notes
|
205
|
+
|
206
|
+
invoice.add_item(0, 4, 10.00, 3, 'Lollipop Consulting', '')
|
207
|
+
```
|
208
|
+
|
209
|
+
Complete field list when reading fields from an invoice: `:affiliate_id, :contact_id, :credit_status, :date_created, :description, :invoice_total, :invoice_type, :job_id, :lead_affiliate_id, :pay_plan_status, :pay_status, :product_sold,
|
210
|
+
:promo_code, :refund_status, :synced, :total_due, :total_paid`
|
211
|
+
|
212
|
+
#### InvoiceItems
|
213
|
+
|
214
|
+
An invoice item is a line item on an invoice. Usage: `invoice.invoice_items`
|
215
|
+
|
216
|
+
To add an invoice item, see the Invoices section above. Complete field list when reading: `:commission_status, :date_created, :description, :discount, :invoice_amt, :invoice_id, :order_item_id`
|
217
|
+
|
218
|
+
Every invoice item belongs to an order item. These are created automatically by Infusionsoft when adding an invoice item. Order item contains quantity and price per unit information. Usage: `invoice.invoice_items.first.order_item`
|
219
|
+
|
220
|
+
Complete field list of order items: `:item_description, :item_name, :item_type, :notes, :order_id, :product_id, :cpu, :ppu, :qty`
|
221
|
+
|
222
|
+
## Refreshing Tokens
|
223
|
+
|
224
|
+
Infusionsoft access tokens expire after a few hours. You can get a new one using Infuser if you have stored the **refresh\_token**. Every time you sign in to Infusionsoft, you get both an access\_token and a refresh\_token. The access\_token is what you use to retrieve data. Once it expires, you use the refresh\_token to get a new access\_token and refresh\_token.
|
225
|
+
|
226
|
+
```ruby
|
227
|
+
r = Infuser::TokenRefresher.new(refresh-token)
|
228
|
+
r.refresh
|
229
|
+
```
|
230
|
+
|
231
|
+
## Issues
|
232
|
+
Submit the issue on Github. I handle gems in my spare time, so if something is time sensitive, be prepared to fork. Pull requests appreciated.
|
233
|
+
|
234
|
+
## Copyright
|
235
|
+
Copyright (c) 2014 David Lesches.
|
236
|
+
|
237
|
+
Original based on code from [Nathan Levitt's gem](https://github.com/nateleavitt/infusionsoft).
|
238
|
+
|
239
|
+
## License
|
240
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software.
|
241
|
+
|
242
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/infuser.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path('../lib/infuser/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |spec|
|
5
|
+
spec.name = 'infuser'
|
6
|
+
spec.summary = %q{Ruby wrapper for the Infusionsoft API}
|
7
|
+
spec.description = 'A Ruby wrapper for the Infusionsoft API'
|
8
|
+
spec.authors = ["David Lesches"]
|
9
|
+
spec.email = ['david@lesches.com']
|
10
|
+
spec.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
|
11
|
+
spec.files = `git ls-files`.split("\n")
|
12
|
+
spec.homepage = 'https://github.com/davidlesches/infuser'
|
13
|
+
spec.require_paths = ['lib']
|
14
|
+
spec.required_rubygems_version = Gem::Requirement.new('>= 1.3.6')
|
15
|
+
|
16
|
+
spec.add_dependency 'activesupport'
|
17
|
+
spec.add_dependency 'rest-client'
|
18
|
+
spec.add_development_dependency 'rake'
|
19
|
+
|
20
|
+
spec.version = Infuser::VERSION
|
21
|
+
end
|
22
|
+
|
data/lib/infuser.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
2
|
+
require 'active_support/core_ext/string/inflections'
|
3
|
+
require 'infuser/helpers/hashie'
|
4
|
+
require 'infuser/version'
|
5
|
+
|
6
|
+
require 'infuser/logger'
|
7
|
+
require 'infuser/exceptions'
|
8
|
+
require 'infuser/configuration'
|
9
|
+
|
10
|
+
require 'infuser/collections/base'
|
11
|
+
require 'infuser/collections/proxy'
|
12
|
+
require 'infuser/collections/child_proxy'
|
13
|
+
require 'infuser/address'
|
14
|
+
require 'infuser/phone'
|
15
|
+
require 'infuser/fax'
|
16
|
+
require 'infuser/email'
|
17
|
+
|
18
|
+
require 'infuser/tables/base'
|
19
|
+
require 'infuser/tables/contact'
|
20
|
+
require 'infuser/tables/company'
|
21
|
+
require 'infuser/tables/invoice'
|
22
|
+
require 'infuser/tables/invoice_item'
|
23
|
+
require 'infuser/tables/order_item'
|
24
|
+
|
25
|
+
require 'infuser/models/base'
|
26
|
+
require 'infuser/contact'
|
27
|
+
require 'infuser/company'
|
28
|
+
require 'infuser/invoice'
|
29
|
+
require 'infuser/invoice_item'
|
30
|
+
require 'infuser/order_item'
|
31
|
+
|
32
|
+
require 'infuser/requester'
|
33
|
+
require 'infuser/client'
|
34
|
+
require 'infuser/token_refresher'
|
35
|
+
|
36
|
+
|
37
|
+
module Infuser
|
38
|
+
|
39
|
+
def self.logger
|
40
|
+
Infuser::Configuration.logger
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
# delete this
|
46
|
+
Infuser::Configuration.configure do |c|
|
47
|
+
c.api_key = 'zyh7cwtt24exdxnmhd82ukta'
|
48
|
+
c.api_secret = 'zHnUXTZMn6'
|
49
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Infuser
|
2
|
+
class Address < Infuser::Collections::Base
|
3
|
+
|
4
|
+
define_mappings({
|
5
|
+
1 => {
|
6
|
+
:street_address1 => :street_address,
|
7
|
+
:street_address2 => :street_address2,
|
8
|
+
:address1_type => :address_type,
|
9
|
+
:city => :city,
|
10
|
+
:state => :state,
|
11
|
+
:country => :country,
|
12
|
+
:postal_code => :postal_code,
|
13
|
+
:zip_four1 => :zip
|
14
|
+
},
|
15
|
+
2 => {
|
16
|
+
:address2_street1 => :street_address,
|
17
|
+
:address2_street2 => :street_address2,
|
18
|
+
:address2_type => :address_type,
|
19
|
+
:city2 => :city,
|
20
|
+
:state2 => :state,
|
21
|
+
:country2 => :country,
|
22
|
+
:postal_code2 => :postal_code,
|
23
|
+
:zip_four2 => :zip
|
24
|
+
},
|
25
|
+
3 => {
|
26
|
+
:address3_street1 => :street_address,
|
27
|
+
:address3_street2 => :street_address2,
|
28
|
+
:address3_type => :address_type,
|
29
|
+
:city3 => :city,
|
30
|
+
:state3 => :state,
|
31
|
+
:country3 => :country,
|
32
|
+
:postal_code3 => :postal_code,
|
33
|
+
:zip_four3 => :zip
|
34
|
+
}
|
35
|
+
})
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Infuser
|
2
|
+
class Client
|
3
|
+
|
4
|
+
TABLES = %w( contact company invoices invoice_items order_items )
|
5
|
+
attr_reader *TABLES.map(&:pluralize)
|
6
|
+
attr_reader :access_token
|
7
|
+
|
8
|
+
def initialize access_token
|
9
|
+
@access_token = access_token || raise(Infuser::ArgumentError, 'You must specify an access token.')
|
10
|
+
setup_associations
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"#<#{self.class.name} access_token: #{access_token}>"
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def setup_associations
|
22
|
+
TABLES.each do |assoc|
|
23
|
+
instance_variable_set "@#{assoc.pluralize}".to_sym, Infuser::Tables.const_get(assoc.classify).new(__client__)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def __client__
|
28
|
+
@__client__ ||= Infuser::Requester.new(access_token)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Infuser
|
2
|
+
module Collections
|
3
|
+
class Base
|
4
|
+
|
5
|
+
def self.define_mappings map
|
6
|
+
@mappings = map
|
7
|
+
@standardized_fields = map.values.map(&:values).flatten.uniq
|
8
|
+
attr_accessor *standardized_fields
|
9
|
+
attr_accessor :id
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.inverted_mappings
|
13
|
+
@inverted_mappings ||= mappings.each_with_object({}) do |(key, value), hash|
|
14
|
+
value.keys.each do |field|
|
15
|
+
hash[field] = key
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.schema
|
21
|
+
inverted_mappings.keys
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.mappings
|
25
|
+
@mappings
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.standardized_fields
|
29
|
+
@standardized_fields
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize data = {}
|
33
|
+
initial_id = data.fetch(:id) { 0 }
|
34
|
+
|
35
|
+
if initial_id > 0
|
36
|
+
self.class.mappings[initial_id].each do |key, value|
|
37
|
+
next if key == value
|
38
|
+
|
39
|
+
define_singleton_method key do
|
40
|
+
send value
|
41
|
+
end
|
42
|
+
|
43
|
+
define_singleton_method "#{key}=" do |*args|
|
44
|
+
send "#{value}=", *args
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
data.each do |key, value|
|
50
|
+
send "#{key}=", value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def data
|
55
|
+
raise(Infuser::ArgumentError, 'Cannot be called if item has no ID') if id.nil?
|
56
|
+
self.class.mappings[id].each_with_object({}) do |(key, value), hash|
|
57
|
+
hash[key] = send(value)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def attributes
|
62
|
+
raise(Infuser::ArgumentError, 'Cannot be called if item has no ID') if id.nil?
|
63
|
+
self.class.mappings[id].each_with_object({}) do |(key, value), hash|
|
64
|
+
hash[value] = send(value)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|