qbo_api 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9705a6a9fde66e76546b9203e846d505dd5d3f15
4
- data.tar.gz: 171efdaf99db8369c5a7abe900ee2be306600a10
3
+ metadata.gz: fa54c3e368cbe905ae5b328cdac7de62ba744f05
4
+ data.tar.gz: d52eaa3b8dfddc1ecafd1e8860235317f972682c
5
5
  SHA512:
6
- metadata.gz: c4ce0818cb0ef23707b903d765d092b6e2a784d1e3cfe2ac570cbe4be31d25a9bf91dc5dfde76281b62279543f31b650c67dd25591f0a1c8c991cf5ea2910da6
7
- data.tar.gz: 8a9007b00ec9464125a5851c97a5f1754cc74472ff2292a0fbc7a505140b89693958632e2757f618988aa25900a7608df6d29d35a2f42f5cc251e38d98217bdc
6
+ metadata.gz: 5df20a423bea757d0ecdecb67d3c096758ebd381d5e61a6175ef52c5959d5d7d8aa2e558fb7b8c60a38be6f74050b301bbe53f01221ef4572af924d6ab961a16
7
+ data.tar.gz: 601d278a518e73d0a29844e5d2f9e73a2eb1388b424a3f87b6398955d6c034292a3cdf108fe753ea949e30ae1d90870710f45f94546f8a1c6f265930a368c03d
data/.travis.yml CHANGED
@@ -1,4 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.2.2
4
- before_install: gem install bundler -v 1.10.6
4
+ - 2.3.0
5
+
6
+ sudo: false
7
+ notifications:
8
+ email:
9
+ - christian@minimul.com
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, token_secret: q.secret, realm_id: q.companyid)
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
- #require 'rack'
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 = "#{entity_path(entity)}?operation=delete"
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/0100_accounting/0060_authentication_and_authorization/oauth_management_api
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
- response = connection(url: APP_CONNECTION_URL).get('/disconnect')
83
+ path = "#{APP_CONNECTION_URL}/disconnect"
84
+ request(:get, path: path)
86
85
  end
87
86
 
88
87
  def reconnect
89
- response = connection(url: APP_CONNECTION_URL).get('/reconnect')
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
@@ -24,5 +24,13 @@ class QboApi
24
24
  def production=(value)
25
25
  @production = value
26
26
  end
27
+
28
+ def request_id
29
+ @request_id ||= false
30
+ end
31
+
32
+ def request_id=(value)
33
+ @request_id = value
34
+ end
27
35
  end
28
36
  end
@@ -73,6 +73,7 @@ class QboApi
73
73
  def supporting_entities
74
74
  %w{
75
75
  Attachable
76
+ Batch
76
77
  CompanyInfo
77
78
  Entitlements
78
79
  ExchangeRate
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
- #500 Internal Server Error An error occured on the server while processing the request. Resubmit request once; if it persists, contact developer support.
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
 
@@ -1,3 +1,3 @@
1
1
  class QboApi
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
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.1.0
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-03-28 00:00:00.000000000 Z
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