qbo_api 1.7.1 → 1.8.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/qbo_api.rb +8 -108
- data/lib/qbo_api/api_methods.rb +148 -0
- data/lib/qbo_api/attachment.rb +1 -8
- data/lib/qbo_api/connection.rb +157 -0
- data/lib/qbo_api/util.rb +0 -15
- data/lib/qbo_api/version.rb +1 -1
- data/qbo_api.gemspec +1 -1
- metadata +15 -17
- data/lib/qbo_api/all.rb +0 -31
- data/lib/qbo_api/builder.rb +0 -21
- data/lib/qbo_api/finder.rb +0 -47
- data/lib/qbo_api/setter.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c9489158fdcc87c75c395c4e51c4f6faa6850c5
|
4
|
+
data.tar.gz: a4ed41891faaa78efd78a540937b0e444287e88e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f35f5150430bab828dc43853cd2640fd81ec1ce175e08600e286489abf79e85f9939e1cebde2ae825874865cd343b312f0b5d94d7e0e8d9be837301ccc16347
|
7
|
+
data.tar.gz: a58e610b9fef2f7f0756534b1898549beb66df6dabc955d75c349db6c391f17b33844fb9ece8b7c9c431d3eddf4178e7f9147ec4cd438d0af3f8edaa1d284768
|
data/lib/qbo_api.rb
CHANGED
@@ -3,31 +3,24 @@ require 'json'
|
|
3
3
|
require 'uri'
|
4
4
|
require 'securerandom'
|
5
5
|
require 'logger'
|
6
|
-
require 'faraday'
|
7
|
-
require 'faraday_middleware'
|
8
|
-
require 'faraday/detailed_logger'
|
9
6
|
require_relative 'qbo_api/configuration'
|
7
|
+
require_relative 'qbo_api/connection'
|
10
8
|
require_relative 'qbo_api/supporting'
|
11
9
|
require_relative 'qbo_api/error'
|
12
10
|
require_relative 'qbo_api/raise_http_exception'
|
13
11
|
require_relative 'qbo_api/entity'
|
14
12
|
require_relative 'qbo_api/util'
|
15
13
|
require_relative 'qbo_api/attachment'
|
16
|
-
require_relative 'qbo_api/
|
17
|
-
require_relative 'qbo_api/builder'
|
18
|
-
require_relative 'qbo_api/finder'
|
19
|
-
require_relative 'qbo_api/all'
|
14
|
+
require_relative 'qbo_api/api_methods'
|
20
15
|
|
21
16
|
class QboApi
|
22
17
|
extend Configuration
|
18
|
+
include Connection
|
23
19
|
include Supporting
|
24
20
|
include Entity
|
25
21
|
include Util
|
26
22
|
include Attachment
|
27
|
-
include
|
28
|
-
include Builder
|
29
|
-
include Finder
|
30
|
-
include All
|
23
|
+
include ApiMethods
|
31
24
|
|
32
25
|
attr_reader :realm_id
|
33
26
|
|
@@ -38,6 +31,7 @@ class QboApi
|
|
38
31
|
V3_ENDPOINT_BASE_URL = 'https://sandbox-quickbooks.api.intuit.com/v3/company/'
|
39
32
|
PAYMENTS_API_BASE_URL = 'https://sandbox.api.intuit.com/quickbooks/v4/payments'
|
40
33
|
APP_CONNECTION_URL = APP_CENTER_BASE + '/api/v1/connection'
|
34
|
+
LOG_TAG = "[QuickBooks]"
|
41
35
|
|
42
36
|
def initialize(token: nil, token_secret: nil, access_token: nil, realm_id:,
|
43
37
|
consumer_key: nil, consumer_secret: nil, endpoint: :accounting)
|
@@ -48,108 +42,15 @@ class QboApi
|
|
48
42
|
@access_token = access_token
|
49
43
|
@realm_id = realm_id
|
50
44
|
@endpoint = endpoint
|
45
|
+
@endpoint_url = get_endpoint
|
51
46
|
end
|
52
47
|
|
53
|
-
def connection(url:
|
54
|
-
@connection ||=
|
55
|
-
faraday.headers['Content-Type'] = 'application/json;charset=UTF-8'
|
56
|
-
faraday.headers['Accept'] = 'application/json'
|
57
|
-
if @token != nil
|
58
|
-
faraday.request :oauth, oauth_data
|
59
|
-
elsif @access_token != nil
|
60
|
-
faraday.request :oauth2, @access_token, token_type: 'bearer'
|
61
|
-
else
|
62
|
-
raise QboApi::Error.new error_body: 'Must set either the token or access_token'
|
63
|
-
end
|
64
|
-
faraday.request :url_encoded
|
65
|
-
faraday.use FaradayMiddleware::RaiseHttpException
|
66
|
-
faraday.response :detailed_logger, QboApi.logger if QboApi.log
|
67
|
-
faraday.adapter Faraday.default_adapter
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def create(entity, payload:, params: nil)
|
72
|
-
request(:post, entity: entity, path: entity_path(entity), payload: payload, params: params)
|
73
|
-
end
|
74
|
-
|
75
|
-
def update(entity, id:, payload:, params: nil)
|
76
|
-
payload.merge!(set_update(entity, id))
|
77
|
-
request(:post, entity: entity, path: entity_path(entity), payload: payload, params: params)
|
78
|
-
end
|
79
|
-
|
80
|
-
def delete(entity, id:)
|
81
|
-
err_msg = "Delete is only for transaction entities. Use .deactivate instead"
|
82
|
-
raise QboApi::NotImplementedError.new, err_msg unless is_transaction_entity?(entity)
|
83
|
-
path = add_params_to_path(path: entity_path(entity), params: { operation: :delete })
|
84
|
-
payload = set_update(entity, id)
|
85
|
-
request(:post, entity: entity, path: path, payload: payload)
|
86
|
-
end
|
87
|
-
|
88
|
-
def deactivate(entity, id:)
|
89
|
-
err_msg = "Deactivate is only for name list entities. Use .delete instead"
|
90
|
-
raise QboApi::NotImplementedError.new, err_msg unless is_name_list_entity?(entity)
|
91
|
-
payload = set_deactivate(entity, id)
|
92
|
-
request(:post, entity: entity, path: entity_path(entity), payload: payload)
|
93
|
-
end
|
94
|
-
|
95
|
-
# https://developer.intuit.com/docs/0100_quickbooks_online/0100_essentials/0085_develop_quickbooks_apps/0004_authentication_and_authorization/oauth_management_api#/Reconnect
|
96
|
-
def disconnect
|
97
|
-
path = "#{APP_CONNECTION_URL}/disconnect"
|
98
|
-
request(:get, path: path)
|
99
|
-
end
|
100
|
-
|
101
|
-
def reconnect
|
102
|
-
path = "#{APP_CONNECTION_URL}/reconnect"
|
103
|
-
request(:get, path: path)
|
104
|
-
end
|
105
|
-
|
106
|
-
def request(method, path:, entity: nil, payload: nil, params: nil)
|
107
|
-
raw_response = connection.send(method) do |req|
|
108
|
-
path = finalize_path(path, method: method, params: params)
|
109
|
-
case method
|
110
|
-
when :get, :delete
|
111
|
-
req.url path
|
112
|
-
when :post, :put
|
113
|
-
req.url path
|
114
|
-
req.body = payload.to_json
|
115
|
-
end
|
116
|
-
end
|
117
|
-
response(raw_response, entity: entity)
|
118
|
-
end
|
119
|
-
|
120
|
-
def response(resp, entity: nil)
|
121
|
-
data = JSON.parse(resp.body)
|
122
|
-
if entity
|
123
|
-
entity_response(data, entity)
|
124
|
-
else
|
125
|
-
data
|
126
|
-
end
|
127
|
-
rescue => e
|
128
|
-
# Catch fetch key errors and just return JSON
|
129
|
-
data
|
48
|
+
def connection(url: @endpoint_url)
|
49
|
+
@connection ||= authorized_json_connection(url)
|
130
50
|
end
|
131
51
|
|
132
52
|
private
|
133
53
|
|
134
|
-
def entity_response(data, entity)
|
135
|
-
if qr = data['QueryResponse']
|
136
|
-
qr.empty? ? nil : qr.fetch(singular(entity))
|
137
|
-
elsif ar = data['AttachableResponse']
|
138
|
-
ar.first.fetch(singular(entity))
|
139
|
-
else
|
140
|
-
data.fetch(singular(entity))
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def oauth_data
|
145
|
-
{
|
146
|
-
consumer_key: @consumer_key,
|
147
|
-
consumer_secret: @consumer_secret,
|
148
|
-
token: @token,
|
149
|
-
token_secret: @token_secret
|
150
|
-
}
|
151
|
-
end
|
152
|
-
|
153
54
|
def get_endpoint
|
154
55
|
prod = self.class.production
|
155
56
|
case @endpoint
|
@@ -159,5 +60,4 @@ class QboApi
|
|
159
60
|
prod ? PAYMENTS_API_BASE_URL.sub("sandbox.", '') : PAYMENTS_API_BASE_URL
|
160
61
|
end
|
161
62
|
end
|
162
|
-
|
163
63
|
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
class QboApi
|
2
|
+
module ApiMethods
|
3
|
+
|
4
|
+
def all(entity, max: 1000, select: nil, inactive: false, params: nil, &block)
|
5
|
+
enumerator = create_all_enumerator(entity, max: max, select: select, inactive: inactive, params: params)
|
6
|
+
|
7
|
+
if block_given?
|
8
|
+
enumerator.each(&block)
|
9
|
+
else
|
10
|
+
enumerator
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def query(query, params: nil)
|
15
|
+
path = "#{realm_id}/query?query=#{CGI.escape(query)}"
|
16
|
+
entity = extract_entity_from_query(query, to_sym: true)
|
17
|
+
request(:get, entity: entity, path: path, params: params)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @example
|
21
|
+
# get(:customer, 5)
|
22
|
+
# @see #get_by_query_filter
|
23
|
+
def get(entity, id_or_query_filter_args, params: nil)
|
24
|
+
if id_or_query_filter_args.is_a?(Array)
|
25
|
+
get_by_query_filter(entity, id_or_query_filter_args, params: params)
|
26
|
+
else
|
27
|
+
path = "#{entity_path(entity)}/#{id_or_query_filter_args}"
|
28
|
+
request(:get, entity: entity, path: path, params: params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# @example
|
33
|
+
# get_by_query_filter(:customer, ["DisplayName", "Dukes Basketball Camp"])
|
34
|
+
# get_by_query_filter(:customer, ["DisplayName", "LIKE", "Dukes%"])
|
35
|
+
# get_by_query_filter(:vendor, ["DisplayName", "IN", "(true, false)"])
|
36
|
+
# get_by_query_filter(:customer, ["DisplayName", "Amy's Bird Sanctuary"])
|
37
|
+
def get_by_query_filter(entity, query_filter_args, params: nil)
|
38
|
+
query_str = get_query_str(entity, query_filter_args)
|
39
|
+
if resp = query(query_str, params: params)
|
40
|
+
resp.size == 1 ? resp[0] : resp
|
41
|
+
else
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def create(entity, payload:, params: nil)
|
47
|
+
request(:post, entity: entity, path: entity_path(entity), payload: payload, params: params)
|
48
|
+
end
|
49
|
+
|
50
|
+
def update(entity, id:, payload:, params: nil)
|
51
|
+
payload.merge!(set_update(entity, id))
|
52
|
+
request(:post, entity: entity, path: entity_path(entity), payload: payload, params: params)
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete(entity, id:)
|
56
|
+
err_msg = "Delete is only for transaction entities. Use .deactivate instead"
|
57
|
+
raise QboApi::NotImplementedError.new, err_msg unless is_transaction_entity?(entity)
|
58
|
+
path = add_params_to_path(path: entity_path(entity), params: { operation: :delete })
|
59
|
+
payload = set_update(entity, id)
|
60
|
+
request(:post, entity: entity, path: path, payload: payload)
|
61
|
+
end
|
62
|
+
|
63
|
+
def deactivate(entity, id:)
|
64
|
+
err_msg = "Deactivate is only for name list entities. Use .delete instead"
|
65
|
+
raise QboApi::NotImplementedError.new, err_msg unless is_name_list_entity?(entity)
|
66
|
+
payload = set_deactivate(entity, id)
|
67
|
+
request(:post, entity: entity, path: entity_path(entity), payload: payload)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def get_query_str(entity, query_filter_args)
|
73
|
+
filterable_field = query_filter_args[0]
|
74
|
+
operator = query_filter_args.size == 2 ? '=' : query_filter_args[1]
|
75
|
+
value = query_filter_args.size == 2 ? query_filter_args[1] : query_filter_args[2]
|
76
|
+
"SELECT * FROM #{singular(entity)} WHERE #{filterable_field} #{operator} #{to_quote_or_not(value)}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_quote_or_not(str)
|
80
|
+
inside_parens_regex = '\(.*\)'
|
81
|
+
if str.match(/^(#{inside_parens_regex}|true|false|CURRENT_DATE)$/)
|
82
|
+
str
|
83
|
+
else
|
84
|
+
%{'#{esc(str)}'}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def create_all_enumerator(entity, max: 1000, select: nil, inactive: false, params: nil)
|
89
|
+
Enumerator.new do |enum_yielder|
|
90
|
+
select = build_all_query(entity, select: select, inactive: inactive)
|
91
|
+
pos = 0
|
92
|
+
begin
|
93
|
+
pos = pos == 0 ? pos + 1 : pos + max
|
94
|
+
results = query(offset_query_string(select, limit: max, offset: pos), params: params)
|
95
|
+
results.each do |entry|
|
96
|
+
enum_yielder.yield(entry)
|
97
|
+
end if results
|
98
|
+
end while (results ? results.size == max : false)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# NOTE(BF): QuickBooks offsets start at 1, but our convention is to index at 0.
|
103
|
+
# That is, to get an offset of index 0, pass in 1, and so forth.
|
104
|
+
def offset_query_string(query_string, limit:, offset:)
|
105
|
+
"#{query_string} MAXRESULTS #{limit} STARTPOSITION #{offset}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def join_or_start_where_clause!(select:)
|
109
|
+
if select.match(/where/i)
|
110
|
+
str = ' AND '
|
111
|
+
else
|
112
|
+
str = ' WHERE '
|
113
|
+
end
|
114
|
+
str
|
115
|
+
end
|
116
|
+
|
117
|
+
def build_all_query(entity, select: nil, inactive: false)
|
118
|
+
select ||= "SELECT * FROM #{singular(entity)}"
|
119
|
+
select += join_or_start_where_clause!(select: select) + 'Active IN ( true, false )' if inactive
|
120
|
+
select
|
121
|
+
end
|
122
|
+
|
123
|
+
def build_update(resp)
|
124
|
+
{ Id: resp['Id'], SyncToken: resp['SyncToken'] }
|
125
|
+
end
|
126
|
+
|
127
|
+
def build_deactivate(entity, resp)
|
128
|
+
payload = build_update(resp).merge('sparse': true, 'Active': false)
|
129
|
+
|
130
|
+
case singular(entity)
|
131
|
+
when 'Account'
|
132
|
+
payload['Name'] = resp['Name']
|
133
|
+
end
|
134
|
+
payload
|
135
|
+
end
|
136
|
+
|
137
|
+
def set_update(entity, id)
|
138
|
+
resp = get(entity, id)
|
139
|
+
build_update(resp)
|
140
|
+
end
|
141
|
+
|
142
|
+
def set_deactivate(entity, id)
|
143
|
+
resp = get(entity, id)
|
144
|
+
build_deactivate(entity, resp)
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
data/lib/qbo_api/attachment.rb
CHANGED
@@ -16,15 +16,8 @@ class QboApi
|
|
16
16
|
response(raw_response, entity: :attachable)
|
17
17
|
end
|
18
18
|
|
19
|
-
private
|
20
|
-
|
21
19
|
def attachment_connection
|
22
|
-
|
23
|
-
multipart_connection = connection.dup
|
24
|
-
multipart_connection.headers['Content-Type'] = 'multipart/form-data'
|
25
|
-
multipart_middleware_index = multipart_connection.builder.handlers.index(Faraday::Request::UrlEncoded) || 1
|
26
|
-
multipart_connection.builder.insert(multipart_middleware_index, Faraday::Request::Multipart)
|
27
|
-
@attachment_connection = multipart_connection
|
20
|
+
@attachment_connection ||= authorized_multipart_connection(@endpoint_url)
|
28
21
|
end
|
29
22
|
|
30
23
|
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require 'faraday/detailed_logger'
|
4
|
+
|
5
|
+
class QboApi
|
6
|
+
module Connection
|
7
|
+
|
8
|
+
def authorized_json_connection(url, headers: nil)
|
9
|
+
headers ||= {}
|
10
|
+
headers['Accept'] ||= 'application/json;charset=UTF-8' # required "we'll only accept JSON". Can be changed to any `+json` media type.
|
11
|
+
headers['Content-Type'] ||= 'application/json' # required when request has a body, else harmless
|
12
|
+
build_connection(url, headers: headers) do |conn|
|
13
|
+
add_authorization_middleware(conn)
|
14
|
+
add_exception_middleware(conn)
|
15
|
+
conn.request :url_encoded
|
16
|
+
add_connection_adapter(conn)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def authorized_multipart_connection(url)
|
21
|
+
headers = { 'Content-Type' => 'multipart/form-data' }
|
22
|
+
build_connection(url, headers: headers) do |conn|
|
23
|
+
add_authorization_middleware(conn)
|
24
|
+
add_exception_middleware(conn)
|
25
|
+
conn.request :multipart
|
26
|
+
add_connection_adapter(conn)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# @example
|
31
|
+
# connection = build_connection('https://oauth.platform.intuit.com', headers: { 'Accept' => 'application/json' }) do |conn|
|
32
|
+
# conn.basic_auth(client_id, client_secret)
|
33
|
+
# conn.request :url_encoded # application/x-www-form-urlencoded
|
34
|
+
# conn.use FaradayMiddleware::ParseJson, parser_options: { symbolize_names: true }
|
35
|
+
# conn.use Faraday::Response::RaiseError
|
36
|
+
# end
|
37
|
+
# raw_response = connection.post {|req|
|
38
|
+
# req.body = { grant_type: :refresh_token, refresh_token: current_refresh_token }
|
39
|
+
# req.url '/oauth2/v1/tokens/bearer'
|
40
|
+
# }
|
41
|
+
def build_connection(url, headers: nil)
|
42
|
+
Faraday.new(url: url) { |conn|
|
43
|
+
conn.response :detailed_logger, QboApi.logger, LOG_TAG if QboApi.log
|
44
|
+
conn.headers.update(headers) if headers
|
45
|
+
yield conn if block_given?
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def request(method, path:, entity: nil, payload: nil, params: nil)
|
50
|
+
raw_response = raw_request(method, conn: connection, path: path, params: params, payload: payload)
|
51
|
+
response(raw_response, entity: entity)
|
52
|
+
end
|
53
|
+
|
54
|
+
def raw_request(method, conn:, path:, payload: nil, params: nil)
|
55
|
+
path = finalize_path(path, method: method, params: params)
|
56
|
+
conn.public_send(method) do |req|
|
57
|
+
case method
|
58
|
+
when :get, :delete
|
59
|
+
req.url path
|
60
|
+
when :post, :put
|
61
|
+
req.url path
|
62
|
+
req.body = payload.to_json
|
63
|
+
else raise ArgumentError, "Unhandled request method '#{method.inspect}'"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def response(resp, entity: nil)
|
69
|
+
data = parse_response_body(resp)
|
70
|
+
entity ? entity_response(data, entity) : data
|
71
|
+
rescue => e
|
72
|
+
QboApi.logger.debug { "#{LOG_TAG} response parsing error: entity=#{entity.inspect} body=#{resp.body.inspect} exception=#{e.inspect}" }
|
73
|
+
data
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse_response_body(resp)
|
77
|
+
body = resp.body
|
78
|
+
case resp.headers['Content-Type']
|
79
|
+
when /json/ then JSON.parse(body)
|
80
|
+
else body
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Part of the OAuth1 API
|
85
|
+
# https://developer.intuit.com/docs/0100_quickbooks_online/0100_essentials/0085_develop_quickbooks_apps/0004_authentication_and_authorization/oauth_management_api#/Reconnect
|
86
|
+
def disconnect
|
87
|
+
path = "#{APP_CONNECTION_URL}/disconnect"
|
88
|
+
request(:get, path: path)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Part of the OAuth1 API
|
92
|
+
# https://developer.intuit.com/docs/0100_quickbooks_online/0100_essentials/0085_develop_quickbooks_apps/0004_authentication_and_authorization/oauth_management_api#/Reconnect
|
93
|
+
def reconnect
|
94
|
+
path = "#{APP_CONNECTION_URL}/reconnect"
|
95
|
+
request(:get, path: path)
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def entity_response(data, entity)
|
101
|
+
entity_name = entity_name(entity)
|
102
|
+
if data.key?('QueryResponse')
|
103
|
+
entity_body = data['QueryResponse']
|
104
|
+
return nil if entity_body.empty?
|
105
|
+
entity_body.fetch(entity_name, data)
|
106
|
+
elsif data.key?('AttachableResponse')
|
107
|
+
entity_body = data['AttachableResponse']
|
108
|
+
entity_body &&= entity_body.first
|
109
|
+
entity_body.fetch(entity_name, data)
|
110
|
+
else
|
111
|
+
entity_body = data
|
112
|
+
entity_body.fetch(entity_name) do
|
113
|
+
QboApi.logger.debug { "#{LOG_TAG} entity name not in response body: entity=#{entity.inspect} entity_name=#{entity_name.inspect} body=#{data.inspect}" }
|
114
|
+
data
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def add_connection_adapter(conn)
|
120
|
+
conn.adapter Faraday.default_adapter
|
121
|
+
end
|
122
|
+
|
123
|
+
def add_exception_middleware(conn)
|
124
|
+
conn.use FaradayMiddleware::RaiseHttpException
|
125
|
+
end
|
126
|
+
|
127
|
+
def add_authorization_middleware(conn)
|
128
|
+
if @token != nil
|
129
|
+
# Part of the OAuth1 API
|
130
|
+
gem 'simple_oauth'
|
131
|
+
require 'simple_oauth'
|
132
|
+
conn.request :oauth, oauth_data
|
133
|
+
elsif @access_token != nil
|
134
|
+
conn.request :oauth2, @access_token, token_type: 'bearer'
|
135
|
+
else
|
136
|
+
raise QboApi::Error.new error_body: 'Must set either the token or access_token'
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Part of the OAuth1 API
|
141
|
+
# Use with simple_oauth OAuth1 middleware
|
142
|
+
# @see #add_authorization_middleware
|
143
|
+
def oauth_data
|
144
|
+
{
|
145
|
+
consumer_key: @consumer_key,
|
146
|
+
consumer_secret: @consumer_secret,
|
147
|
+
token: @token,
|
148
|
+
token_secret: @token_secret
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
def entity_name(entity)
|
153
|
+
singular(entity)
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
data/lib/qbo_api/util.rb
CHANGED
@@ -54,21 +54,6 @@ class QboApi
|
|
54
54
|
uri.to_s
|
55
55
|
end
|
56
56
|
|
57
|
-
def join_or_start_where_clause!(select:)
|
58
|
-
if select.match(/where/i)
|
59
|
-
str = ' AND '
|
60
|
-
else
|
61
|
-
str = ' WHERE '
|
62
|
-
end
|
63
|
-
str
|
64
|
-
end
|
65
|
-
|
66
|
-
def build_all_query(entity, select: nil, inactive: false)
|
67
|
-
select ||= "SELECT * FROM #{singular(entity)}"
|
68
|
-
select += join_or_start_where_clause!(select: select) + 'Active IN ( true, false )' if inactive
|
69
|
-
select
|
70
|
-
end
|
71
|
-
|
72
57
|
end
|
73
58
|
end
|
74
59
|
|
data/lib/qbo_api/version.rb
CHANGED
data/qbo_api.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency 'webmock'
|
25
25
|
spec.add_development_dependency 'sinatra'
|
26
26
|
spec.add_development_dependency 'rack-oauth2'
|
27
|
+
spec.add_development_dependency 'simple_oauth'
|
27
28
|
spec.add_development_dependency 'omniauth'
|
28
29
|
spec.add_development_dependency 'omniauth-quickbooks'
|
29
30
|
spec.add_development_dependency 'shotgun'
|
@@ -33,6 +34,5 @@ Gem::Specification.new do |spec|
|
|
33
34
|
spec.add_runtime_dependency 'faraday'
|
34
35
|
spec.add_runtime_dependency 'faraday_middleware'
|
35
36
|
spec.add_runtime_dependency 'faraday-detailed_logger'
|
36
|
-
spec.add_runtime_dependency 'simple_oauth'
|
37
37
|
spec.add_runtime_dependency 'nokogiri'
|
38
38
|
end
|
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.8.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: 2018-03-
|
11
|
+
date: 2018-03-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -95,7 +95,7 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: simple_oauth
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
@@ -109,7 +109,7 @@ dependencies:
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name: omniauth
|
112
|
+
name: omniauth
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - ">="
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: omniauth-quickbooks
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
@@ -137,7 +137,7 @@ dependencies:
|
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: shotgun
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - ">="
|
@@ -151,7 +151,7 @@ dependencies:
|
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
154
|
+
name: dotenv
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
157
|
- - ">="
|
@@ -165,7 +165,7 @@ dependencies:
|
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
|
-
name:
|
168
|
+
name: vcr
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
170
170
|
requirements:
|
171
171
|
- - ">="
|
@@ -179,13 +179,13 @@ dependencies:
|
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
|
-
name:
|
182
|
+
name: awesome_print
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
184
184
|
requirements:
|
185
185
|
- - ">="
|
186
186
|
- !ruby/object:Gem::Version
|
187
187
|
version: '0'
|
188
|
-
type: :
|
188
|
+
type: :development
|
189
189
|
prerelease: false
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
191
191
|
requirements:
|
@@ -193,7 +193,7 @@ dependencies:
|
|
193
193
|
- !ruby/object:Gem::Version
|
194
194
|
version: '0'
|
195
195
|
- !ruby/object:Gem::Dependency
|
196
|
-
name:
|
196
|
+
name: faraday
|
197
197
|
requirement: !ruby/object:Gem::Requirement
|
198
198
|
requirements:
|
199
199
|
- - ">="
|
@@ -207,7 +207,7 @@ dependencies:
|
|
207
207
|
- !ruby/object:Gem::Version
|
208
208
|
version: '0'
|
209
209
|
- !ruby/object:Gem::Dependency
|
210
|
-
name:
|
210
|
+
name: faraday_middleware
|
211
211
|
requirement: !ruby/object:Gem::Requirement
|
212
212
|
requirements:
|
213
213
|
- - ">="
|
@@ -221,7 +221,7 @@ dependencies:
|
|
221
221
|
- !ruby/object:Gem::Version
|
222
222
|
version: '0'
|
223
223
|
- !ruby/object:Gem::Dependency
|
224
|
-
name:
|
224
|
+
name: faraday-detailed_logger
|
225
225
|
requirement: !ruby/object:Gem::Requirement
|
226
226
|
requirements:
|
227
227
|
- - ">="
|
@@ -272,15 +272,13 @@ files:
|
|
272
272
|
- example/views/oauth2.erb
|
273
273
|
- example/views/oauth2_redirect.erb
|
274
274
|
- lib/qbo_api.rb
|
275
|
-
- lib/qbo_api/
|
275
|
+
- lib/qbo_api/api_methods.rb
|
276
276
|
- lib/qbo_api/attachment.rb
|
277
|
-
- lib/qbo_api/builder.rb
|
278
277
|
- lib/qbo_api/configuration.rb
|
278
|
+
- lib/qbo_api/connection.rb
|
279
279
|
- lib/qbo_api/entity.rb
|
280
280
|
- lib/qbo_api/error.rb
|
281
|
-
- lib/qbo_api/finder.rb
|
282
281
|
- lib/qbo_api/raise_http_exception.rb
|
283
|
-
- lib/qbo_api/setter.rb
|
284
282
|
- lib/qbo_api/supporting.rb
|
285
283
|
- lib/qbo_api/util.rb
|
286
284
|
- lib/qbo_api/version.rb
|
data/lib/qbo_api/all.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
class QboApi
|
2
|
-
module All
|
3
|
-
|
4
|
-
def all(entity, max: 1000, select: nil, inactive: false, &block)
|
5
|
-
enumerator = create_all_enumerator(entity, max: max, select: select, inactive: inactive)
|
6
|
-
|
7
|
-
if block_given?
|
8
|
-
enumerator.each(&block)
|
9
|
-
else
|
10
|
-
enumerator
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def create_all_enumerator(entity, max: 1000, select: nil, inactive: false)
|
17
|
-
Enumerator.new do |enum_yielder|
|
18
|
-
select = build_all_query(entity, select: select, inactive: inactive)
|
19
|
-
pos = 0
|
20
|
-
begin
|
21
|
-
pos = pos == 0 ? pos + 1 : pos + max
|
22
|
-
results = query("#{select} MAXRESULTS #{max} STARTPOSITION #{pos}")
|
23
|
-
results.each do |entry|
|
24
|
-
enum_yielder.yield(entry)
|
25
|
-
end if results
|
26
|
-
end while (results ? results.size == max : false)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|
31
|
-
end
|
data/lib/qbo_api/builder.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
class QboApi
|
2
|
-
module Builder
|
3
|
-
|
4
|
-
private
|
5
|
-
|
6
|
-
def build_update(resp)
|
7
|
-
{ Id: resp['Id'], SyncToken: resp['SyncToken'] }
|
8
|
-
end
|
9
|
-
|
10
|
-
def build_deactivate(entity, resp)
|
11
|
-
payload = build_update(resp).merge('sparse': true, 'Active': false)
|
12
|
-
|
13
|
-
case singular(entity)
|
14
|
-
when 'Account'
|
15
|
-
payload['Name'] = resp['Name']
|
16
|
-
end
|
17
|
-
payload
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
end
|
data/lib/qbo_api/finder.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
class QboApi
|
2
|
-
module Finder
|
3
|
-
|
4
|
-
def query(query, params: nil)
|
5
|
-
path = "#{realm_id}/query?query=#{CGI.escape(query)}"
|
6
|
-
entity = extract_entity_from_query(query, to_sym: true)
|
7
|
-
request(:get, entity: entity, path: path, params: params)
|
8
|
-
end
|
9
|
-
|
10
|
-
def get(entity, type, params: nil)
|
11
|
-
if type.is_a?(Array)
|
12
|
-
query_str = get_query_str(entity, type)
|
13
|
-
if resp = query(query_str, params: params)
|
14
|
-
resp.size == 1 ? resp[0] : resp
|
15
|
-
else
|
16
|
-
false
|
17
|
-
end
|
18
|
-
else
|
19
|
-
path = "#{entity_path(entity)}/#{type}"
|
20
|
-
request(:get, entity: entity, path: path, params: params)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def to_quote_or_not(str)
|
25
|
-
inside_parens_regex = '\(.*\)'
|
26
|
-
if str.match(/^(#{inside_parens_regex}|true|false|CURRENT_DATE)$/)
|
27
|
-
str
|
28
|
-
else
|
29
|
-
%{'#{esc(str)}'}
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def get_query_str(entity, type)
|
36
|
-
if type.size == 2
|
37
|
-
operator = '='
|
38
|
-
value = type[1]
|
39
|
-
else
|
40
|
-
operator = type[1]
|
41
|
-
value = type[2]
|
42
|
-
end
|
43
|
-
"SELECT * FROM #{singular(entity)} WHERE #{type[0]} #{operator} #{to_quote_or_not(value)}"
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
47
|
-
end
|
data/lib/qbo_api/setter.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
class QboApi
|
2
|
-
module Setter
|
3
|
-
|
4
|
-
private
|
5
|
-
|
6
|
-
def set_update(entity, id)
|
7
|
-
resp = get(entity, id)
|
8
|
-
build_update(resp)
|
9
|
-
end
|
10
|
-
|
11
|
-
def set_deactivate(entity, id)
|
12
|
-
resp = get(entity, id)
|
13
|
-
build_deactivate(entity, resp)
|
14
|
-
end
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|