qbo_api 1.1.0 → 1.2.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/.travis.yml +6 -1
- data/README.md +90 -1
- data/example/app.rb +20 -1
- data/lib/qbo_api.rb +11 -11
- data/lib/qbo_api/configuration.rb +8 -0
- data/lib/qbo_api/entity.rb +1 -0
- data/lib/qbo_api/error.rb +5 -1
- data/lib/qbo_api/raise_http_exception.rb +2 -2
- data/lib/qbo_api/supporting.rb +21 -0
- data/lib/qbo_api/util.rb +22 -0
- data/lib/qbo_api/version.rb +1 -1
- data/qbo_api.gemspec +1 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa54c3e368cbe905ae5b328cdac7de62ba744f05
|
4
|
+
data.tar.gz: d52eaa3b8dfddc1ecafd1e8860235317f972682c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5df20a423bea757d0ecdecb67d3c096758ebd381d5e61a6175ef52c5959d5d7d8aa2e558fb7b8c60a38be6f74050b301bbe53f01221ef4572af924d6ab961a16
|
7
|
+
data.tar.gz: 601d278a518e73d0a29844e5d2f9e73a2eb1388b424a3f87b6398955d6c034292a3cdf108fe753ea949e30ae1d90870710f45f94546f8a1c6f265930a368c03d
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -36,7 +36,26 @@ Or install it yourself as:
|
|
36
36
|
### Initialize
|
37
37
|
```ruby
|
38
38
|
q = account.qbo_account # or wherever you are storing the OAuth creds
|
39
|
-
qbo_api = QboApi.new(token: q.token,
|
39
|
+
qbo_api = QboApi.new(token: q.token,
|
40
|
+
token_secret: q.secret,
|
41
|
+
realm_id: q.companyid,
|
42
|
+
consumer_key: '*****',
|
43
|
+
consumer_secret: '********')
|
44
|
+
```
|
45
|
+
|
46
|
+
### Super fast way to use QboApi no matter your current tech stack as long as Ruby > 2.2.2 is installed
|
47
|
+
```
|
48
|
+
- cd ~/<local dir>
|
49
|
+
- git clone git@github.com:minimul/qbo_api.git && cd qbo_api
|
50
|
+
- bundle
|
51
|
+
- bin/console
|
52
|
+
- QboApi.production = true
|
53
|
+
- qboapi = QboApi.new(token: "qyprd2uvCOdRq8xzoSSiiiiii",
|
54
|
+
token_secret:"g8wcyQEtwxxxxxxm",
|
55
|
+
realm_id: "12314xxxxxx7",
|
56
|
+
consumer_key: "qyprdwzcxxxxxxbIWsIMIy9PYI",
|
57
|
+
consumer_secret: "CyDN4wpxxxxxxxPMv7hDhmh4")
|
58
|
+
- qboapi.get :customer, 1
|
40
59
|
```
|
41
60
|
|
42
61
|
### Configuration options
|
@@ -52,6 +71,10 @@ QboApi.log = true
|
|
52
71
|
```ruby
|
53
72
|
QboApi.logger = Rails.logger
|
54
73
|
```
|
74
|
+
- To run all create, modify, and delete requests with unique [RequestIds](https://developer.intuit.com/hub/blog/2015/04/06/15346).
|
75
|
+
```ruby
|
76
|
+
QboApi.request_id = true
|
77
|
+
```
|
55
78
|
|
56
79
|
### Create
|
57
80
|
```ruby
|
@@ -114,6 +137,60 @@ QboApi.logger = Rails.logger
|
|
114
137
|
p ids
|
115
138
|
```
|
116
139
|
|
140
|
+
### Batch operations (limit 30 operations in 1 batch request)
|
141
|
+
```ruby
|
142
|
+
payload = {
|
143
|
+
"BatchItemRequest":
|
144
|
+
[
|
145
|
+
{
|
146
|
+
"bId": "bid1",
|
147
|
+
"operation": "create",
|
148
|
+
"Vendor": {
|
149
|
+
"DisplayName": "Smith Family Store"
|
150
|
+
}
|
151
|
+
}, {
|
152
|
+
"bId": "bid2",
|
153
|
+
"operation": "delete",
|
154
|
+
"Invoice": {
|
155
|
+
"Id": "129",
|
156
|
+
"SyncToken": "0"
|
157
|
+
}
|
158
|
+
}
|
159
|
+
]
|
160
|
+
}
|
161
|
+
response = api.batch(payload)
|
162
|
+
expect(response['BatchItemResponse'].size).to eq 2
|
163
|
+
expect(batch_response.detect{ |b| b["bId"] == "bid1" }["Vendor"]["DisplayName"]).to eq "Smith Family Store"
|
164
|
+
```
|
165
|
+
|
166
|
+
### Reports
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
params = { start_date: '2015-01-01', end_date: '2015-07-31', customer: 1, summarize_column_by: 'Customers' }
|
170
|
+
response = api.reports(name: 'ProfitAndLoss', params: params)
|
171
|
+
p response["Header"]["ReportName"]) #=> 'ProfitAndLoss'
|
172
|
+
```
|
173
|
+
|
174
|
+
### Reconnect
|
175
|
+
|
176
|
+
See [docs](https://developer.intuit.com/docs/0100_quickbooks_online/0100_essentials/0085_develop_quickbooks_apps/0004_authentication_and_authorization/oauth_management_api#/Reconnect)
|
177
|
+
```ruby
|
178
|
+
response = qbo_api.reconnect
|
179
|
+
#=> if response['ErrorCode'] == 0
|
180
|
+
#=> p response['OAuthToken'] #=> rewq23423424afadsdfs==
|
181
|
+
#=> p response['OAuthTokenSecret'] #=> ertwwetu12345312005343453yy=Fg
|
182
|
+
```
|
183
|
+
|
184
|
+
### Disconnect
|
185
|
+
|
186
|
+
See [docs](https://developer.intuit.com/docs/0100_quickbooks_online/0100_essentials/0085_develop_quickbooks_apps/0004_authentication_and_authorization/oauth_management_api#/Disconnect)
|
187
|
+
```ruby
|
188
|
+
response = qbo_api.disconnect
|
189
|
+
#=> if response['ErrorCode'] == 0
|
190
|
+
#=> # Successful disconnect
|
191
|
+
```
|
192
|
+
|
193
|
+
|
117
194
|
### Respond to an error
|
118
195
|
```ruby
|
119
196
|
customer = { DisplayName: 'Weiskopf Consulting' }
|
@@ -178,6 +255,9 @@ export QBO_API_CONSUMER_SECRET=<Your QuickBooks apps consumer secret>
|
|
178
255
|
- You should see "Dukes Basketball Camp" displayed
|
179
256
|
- Checkout [`example/app.rb`](https://github.com/minimul/qbo_api/blob/master/example/app.rb) to see what is going on under the hood.
|
180
257
|
|
258
|
+
## Webhooks
|
259
|
+
- <a href="http://minimul.com/getting-started-with-quickbooks-online-webhooks.html" target="_blank">Check out this tutorial and screencast on handling a webhook request</a>. Also checkout [`example/app.rb`](https://github.com/minimul/qbo_api/blob/master/example/app.rb) for the request handling code.
|
260
|
+
|
181
261
|
## Contributing
|
182
262
|
|
183
263
|
Bug reports and pull requests are welcome on GitHub at https://github.com/minimul/qbo_api.
|
@@ -201,6 +281,15 @@ export QBO_API_COMPANY_ID=12345
|
|
201
281
|
- All specs that require interaction with the API must be recorded against your personal QuickBooks sandbox. More coming on how to create or modifying existing specs against your sandbox.
|
202
282
|
- <a href="http://minimul.com/the-modern-ruby-quickbooks-client-contributing.html" target="_blank">Check out this tutorial and screencast on contributing to qbo_api</a>.
|
203
283
|
|
284
|
+
#### Protip: Once your .env file is completely filled out you can use the console to play around in your sandbox
|
285
|
+
```
|
286
|
+
bin/console
|
287
|
+
>> require_relative 'spec/spec_helper'
|
288
|
+
>> VCR.configure { |c| c.allow_http_connections_when_no_cassette = true }
|
289
|
+
>> q = QboApi.new(creds.to_h)
|
290
|
+
>> q.get :customer, 1
|
291
|
+
```
|
292
|
+
|
204
293
|
## License
|
205
294
|
|
206
295
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/example/app.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require "bundler/setup"
|
2
2
|
require 'sinatra'
|
3
3
|
require 'json'
|
4
|
+
require 'openssl'
|
5
|
+
require 'base64'
|
4
6
|
require 'omniauth'
|
5
7
|
require 'omniauth-quickbooks'
|
6
8
|
require 'dotenv'
|
@@ -10,13 +12,22 @@ Dotenv.load "#{__dir__}/../.env"
|
|
10
12
|
PORT = 9393
|
11
13
|
CONSUMER_KEY = ENV['QBO_API_CONSUMER_KEY']
|
12
14
|
CONSUMER_SECRET = ENV['QBO_API_CONSUMER_SECRET']
|
15
|
+
VERIFIER_TOKEN = ENV['QBO_API_VERIFIER_TOKEN']
|
13
16
|
|
14
17
|
set :port, PORT
|
15
|
-
use Rack::Session::Cookie
|
18
|
+
use Rack::Session::Cookie, secret: '34233adasf'
|
16
19
|
use OmniAuth::Builder do
|
17
20
|
provider :quickbooks, CONSUMER_KEY, CONSUMER_SECRET
|
18
21
|
end
|
19
22
|
|
23
|
+
helpers do
|
24
|
+
def verify_webhook(data, hmac_header)
|
25
|
+
digest = OpenSSL::Digest.new('sha256')
|
26
|
+
calculated_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, VERIFIER_TOKEN, data)).strip
|
27
|
+
calculated_hmac == hmac_header
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
20
31
|
get '/' do
|
21
32
|
@app_center = QboApi::APP_CENTER_BASE
|
22
33
|
@auth_data = oauth_data
|
@@ -32,6 +43,14 @@ get '/customer/:id' do
|
|
32
43
|
erb :customer
|
33
44
|
end
|
34
45
|
|
46
|
+
post '/webhooks' do
|
47
|
+
request.body.rewind
|
48
|
+
data = request.body.read
|
49
|
+
puts JSON.parse data
|
50
|
+
verified = verify_webhook(data, env['HTTP_INTUIT_SIGNATURE'])
|
51
|
+
puts "Verified: #{verified}"
|
52
|
+
end
|
53
|
+
|
35
54
|
def oauth_data
|
36
55
|
{
|
37
56
|
consumer_key: CONSUMER_KEY,
|
data/lib/qbo_api.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'qbo_api/version'
|
2
2
|
require 'json'
|
3
|
-
|
3
|
+
require 'uri'
|
4
|
+
require 'securerandom'
|
4
5
|
require 'logger'
|
5
6
|
require 'faraday'
|
6
7
|
require 'faraday_middleware'
|
7
8
|
require 'faraday/detailed_logger'
|
8
9
|
require_relative 'qbo_api/configuration'
|
10
|
+
require_relative 'qbo_api/supporting'
|
9
11
|
require_relative 'qbo_api/error'
|
10
12
|
require_relative 'qbo_api/raise_http_exception'
|
11
13
|
require_relative 'qbo_api/entity'
|
@@ -13,6 +15,7 @@ require_relative 'qbo_api/util'
|
|
13
15
|
|
14
16
|
class QboApi
|
15
17
|
extend Configuration
|
18
|
+
include Supporting
|
16
19
|
include Entity
|
17
20
|
include Util
|
18
21
|
attr_reader :realm_id
|
@@ -53,11 +56,6 @@ class QboApi
|
|
53
56
|
request(:get, entity: entity, path: path)
|
54
57
|
end
|
55
58
|
|
56
|
-
def cdc(entities:, changed_since:)
|
57
|
-
path = "#{realm_id}/cdc?entities=#{entities}&changedSince=#{cdc_time(changed_since)}"
|
58
|
-
request(:get, path: path)
|
59
|
-
end
|
60
|
-
|
61
59
|
def get(entity, id)
|
62
60
|
path = "#{entity_path(entity)}/#{id}"
|
63
61
|
request(:get, entity: entity, path: path)
|
@@ -74,19 +72,21 @@ class QboApi
|
|
74
72
|
|
75
73
|
def delete(entity, id:)
|
76
74
|
raise QboApi::NotImplementedError unless is_transaction_entity?(entity)
|
77
|
-
path =
|
75
|
+
path = add_params_to_path(path: entity_path(entity), params: { operation: :delete })
|
78
76
|
payload = set_update(entity, id)
|
79
77
|
request(:post, entity: entity, path: path, payload: payload)
|
80
78
|
end
|
81
79
|
|
82
80
|
# TODO: Need specs for disconnect and reconnect
|
83
|
-
# https://developer.intuit.com/docs/
|
81
|
+
# https://developer.intuit.com/docs/0100_quickbooks_online/0100_essentials/0085_develop_quickbooks_apps/0004_authentication_and_authorization/oauth_management_api#/Reconnect
|
84
82
|
def disconnect
|
85
|
-
|
83
|
+
path = "#{APP_CONNECTION_URL}/disconnect"
|
84
|
+
request(:get, path: path)
|
86
85
|
end
|
87
86
|
|
88
87
|
def reconnect
|
89
|
-
|
88
|
+
path = "#{APP_CONNECTION_URL}/reconnect"
|
89
|
+
request(:get, path: path)
|
90
90
|
end
|
91
91
|
|
92
92
|
def all(entity, max: 1000, select: nil, &block)
|
@@ -107,7 +107,7 @@ class QboApi
|
|
107
107
|
when :get, :delete
|
108
108
|
req.url URI.encode(path)
|
109
109
|
when :post, :put
|
110
|
-
req.url path
|
110
|
+
req.url add_request_id_to(path)
|
111
111
|
req.body = JSON.generate(payload)
|
112
112
|
end
|
113
113
|
end
|
data/lib/qbo_api/entity.rb
CHANGED
data/lib/qbo_api/error.rb
CHANGED
@@ -4,7 +4,8 @@
|
|
4
4
|
#401 Unauthorized Authentication or authorization has failed.
|
5
5
|
#403 Forbidden The resource is forbidden.
|
6
6
|
#404 Not Found The resource is not found.
|
7
|
-
#
|
7
|
+
#429 Too Many Requests API Throttling/ Rate limiting
|
8
|
+
#500 Internal Server Error An error occurred on the server while processing the request. Resubmit request once; if it persists, contact developer support.
|
8
9
|
#503 Service Unavailable The service is temporarily unavailable.
|
9
10
|
# Custom error class for rescuing from all QuickBooks Online errors
|
10
11
|
class QboApi
|
@@ -33,6 +34,9 @@ class QboApi
|
|
33
34
|
# Raised when QuickBooks Online returns the HTTP status code 404
|
34
35
|
class NotFound < Error; end
|
35
36
|
|
37
|
+
# Raised when QuickBooks Online returns the HTTP status code 429
|
38
|
+
class TooManyRequests < Error; end
|
39
|
+
|
36
40
|
# Raised when QuickBooks Online returns the HTTP status code 500
|
37
41
|
class InternalServerError < Error; end
|
38
42
|
|
@@ -9,8 +9,6 @@ module FaradayMiddleware
|
|
9
9
|
@app.call(env).on_complete do |response|
|
10
10
|
case response.status
|
11
11
|
when 200
|
12
|
-
# 200 responses can have errors
|
13
|
-
raise QboApi::BadRequest.new(error_message(response)) if response.body =~ /Fault.*Error.*Message/
|
14
12
|
when 400
|
15
13
|
raise QboApi::BadRequest.new(error_message(response))
|
16
14
|
when 401
|
@@ -19,6 +17,8 @@ module FaradayMiddleware
|
|
19
17
|
raise QboApi::Forbidden.new(error_message(response))
|
20
18
|
when 404
|
21
19
|
raise QboApi::NotFound.new(error_message(response))
|
20
|
+
when 429
|
21
|
+
raise QboApi::TooManyRequests.new(error_message(response))
|
22
22
|
when 500
|
23
23
|
raise QboApi::InternalServerError.new(error_message(response))
|
24
24
|
when 503
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class QboApi
|
2
|
+
module Supporting
|
3
|
+
|
4
|
+
def cdc(entities:, changed_since:)
|
5
|
+
path = "#{realm_id}/cdc?entities=#{entities}&changedSince=#{cdc_time(changed_since)}"
|
6
|
+
request(:get, path: path)
|
7
|
+
end
|
8
|
+
|
9
|
+
def batch(payload)
|
10
|
+
path = "#{realm_id}/batch"
|
11
|
+
request(:post, path: path, payload: payload)
|
12
|
+
end
|
13
|
+
|
14
|
+
def reports(name:, params: nil)
|
15
|
+
path = "#{realm_id}/reports/#{name}"
|
16
|
+
path = add_params_to_path(path: path, params: params) if params
|
17
|
+
request(:get, path: path)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
data/lib/qbo_api/util.rb
CHANGED
@@ -7,6 +7,28 @@ class QboApi
|
|
7
7
|
time.to_s.sub(' ', 'T').sub(' ', '').insert(-3, ':')
|
8
8
|
end
|
9
9
|
end
|
10
|
+
|
11
|
+
def uuid
|
12
|
+
SecureRandom.uuid
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_request_id_to(path)
|
16
|
+
if QboApi.request_id
|
17
|
+
add_params_to_path(path: path, params: { "requestid" => uuid })
|
18
|
+
else
|
19
|
+
path
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_params_to_path(path:, params:)
|
24
|
+
uri = URI.parse(path)
|
25
|
+
params.each do |p|
|
26
|
+
new_query_ar = URI.decode_www_form(uri.query || '') << p.to_a
|
27
|
+
uri.query = URI.encode_www_form(new_query_ar)
|
28
|
+
end
|
29
|
+
uri.to_s
|
30
|
+
end
|
31
|
+
|
10
32
|
end
|
11
33
|
end
|
12
34
|
|
data/lib/qbo_api/version.rb
CHANGED
data/qbo_api.gemspec
CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_development_dependency 'omniauth-quickbooks'
|
28
28
|
spec.add_development_dependency 'dotenv'
|
29
29
|
spec.add_development_dependency 'vcr'
|
30
|
+
spec.add_development_dependency 'awesome_print'
|
30
31
|
spec.add_runtime_dependency 'faraday'
|
31
32
|
spec.add_runtime_dependency 'faraday_middleware'
|
32
33
|
spec.add_runtime_dependency 'faraday-detailed_logger'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qbo_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christian Pelczarski
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -136,6 +136,20 @@ dependencies:
|
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: awesome_print
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
154
|
name: faraday
|
141
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -229,6 +243,7 @@ files:
|
|
229
243
|
- lib/qbo_api/entity.rb
|
230
244
|
- lib/qbo_api/error.rb
|
231
245
|
- lib/qbo_api/raise_http_exception.rb
|
246
|
+
- lib/qbo_api/supporting.rb
|
232
247
|
- lib/qbo_api/util.rb
|
233
248
|
- lib/qbo_api/version.rb
|
234
249
|
- qbo_api.gemspec
|