octobat 0.0.12 → 2.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 +4 -4
- data/.gitignore +1 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +20 -10
- data/History.txt +33 -0
- data/VERSION +1 -1
- data/lib/octobat.rb +95 -19
- data/lib/octobat/api_operations/delete.rb +1 -0
- data/lib/octobat/api_operations/list.rb +16 -9
- data/lib/octobat/api_operations/update.rb +7 -4
- data/lib/octobat/api_resource.rb +5 -2
- data/lib/octobat/{payment_mode.rb → checkout.rb} +1 -1
- data/lib/octobat/coupon.rb +32 -0
- data/lib/octobat/credit_note.rb +35 -0
- data/lib/octobat/credit_note_numbering_sequence.rb +13 -0
- data/lib/octobat/customer.rb +10 -2
- data/lib/octobat/document.rb +17 -0
- data/lib/octobat/document_email_template.rb +19 -0
- data/lib/octobat/document_language.rb +19 -0
- data/lib/octobat/document_template.rb +33 -0
- data/lib/octobat/emails_setting.rb +19 -0
- data/lib/octobat/errors/api_connection_error.rb +1 -1
- data/lib/octobat/errors/api_error.rb +1 -1
- data/lib/octobat/errors/authentication_error.rb +1 -1
- data/lib/octobat/errors/invalid_request_error.rb +2 -4
- data/lib/octobat/errors/octobat_error.rb +31 -5
- data/lib/octobat/errors/octobat_lib_error.rb +20 -0
- data/lib/octobat/exports_setting.rb +19 -0
- data/lib/octobat/invoice.rb +61 -12
- data/lib/octobat/invoice_numbering_sequence.rb +18 -0
- data/lib/octobat/item.rb +64 -0
- data/lib/octobat/list_object.rb +12 -4
- data/lib/octobat/octobat_object.rb +13 -4
- data/lib/octobat/payment_recipient.rb +7 -0
- data/lib/octobat/{numbering_sequence.rb → payment_recipient_reference.rb} +1 -1
- data/lib/octobat/payment_source.rb +16 -0
- data/lib/octobat/{invoice_item.rb → tax_evidence.rb} +1 -1
- data/lib/octobat/tax_evidence_request.rb +5 -0
- data/lib/octobat/tax_region_setting.rb +27 -0
- data/lib/octobat/transaction.rb +11 -0
- data/lib/octobat/util.rb +28 -10
- data/lib/octobat/version.rb +1 -1
- data/octobat.gemspec +3 -3
- metadata +33 -45
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 041400ae286b51b2e0aaabcc19780b1b7e422640
|
4
|
+
data.tar.gz: 3db20d4d584da54c530ece341ce3451b11ef5bcf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cf9162c36bfa08b72dc53e069053a9d88513e438ffec9f49d42855a0b7259e756afdd808682e0c9c32844302777f6a9bfe54bd99f9237049b069d356143ceeb
|
7
|
+
data.tar.gz: 3b4792604be22a61edaa987e98da891801ca187ec8ecc241da9d15bb0dfc09df734525b972d6cc253a2ed2c1df726d1d5d153d74012155b59aee55aacce8dced
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
2
|
gemspec
|
3
3
|
|
4
|
-
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('
|
4
|
+
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new('2.0.0')
|
5
5
|
gem 'i18n', '< 0.7'
|
6
6
|
gem 'rest-client', '~> 1.6.8'
|
7
|
+
gem 'mime-types', '< 3.0'
|
7
8
|
gem 'activesupport', '~> 3.2'
|
8
9
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,23 +1,33 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
octobat (0.0
|
5
|
-
|
6
|
-
mime-types (>= 1.25, < 3.0)
|
7
|
-
rest-client (~> 1.4)
|
4
|
+
octobat (2.0.0)
|
5
|
+
rest-client (>= 1.4, < 4.0)
|
8
6
|
|
9
7
|
GEM
|
10
8
|
remote: https://rubygems.org/
|
11
9
|
specs:
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
10
|
+
domain_name (0.5.20161129)
|
11
|
+
unf (>= 0.0.5, < 1.0.0)
|
12
|
+
http-cookie (1.0.3)
|
13
|
+
domain_name (~> 0.5)
|
14
|
+
mime-types (3.1)
|
15
|
+
mime-types-data (~> 3.2015)
|
16
|
+
mime-types-data (3.2016.0521)
|
17
|
+
netrc (0.11.0)
|
18
|
+
rest-client (2.0.0)
|
19
|
+
http-cookie (>= 1.0.2, < 2.0)
|
20
|
+
mime-types (>= 1.16, < 4.0)
|
21
|
+
netrc (~> 0.8)
|
22
|
+
unf (0.1.4)
|
23
|
+
unf_ext
|
24
|
+
unf_ext (0.0.7.2)
|
18
25
|
|
19
26
|
PLATFORMS
|
20
27
|
ruby
|
21
28
|
|
22
29
|
DEPENDENCIES
|
23
30
|
octobat!
|
31
|
+
|
32
|
+
BUNDLED WITH
|
33
|
+
1.13.7
|
data/History.txt
CHANGED
@@ -1,3 +1,36 @@
|
|
1
|
+
=== 2.0.0 2017-01-23
|
2
|
+
* 1 major enhancement:
|
3
|
+
* Bump to Octobat v2 API endpoint
|
4
|
+
|
5
|
+
=== 0.0.12 2016-04-13
|
6
|
+
* 1 minor enhancement:
|
7
|
+
* Add pagination helpers
|
8
|
+
|
9
|
+
=== 0.0.9 2016-04-13
|
10
|
+
* 1 major enhancement
|
11
|
+
* Can initialize Octobat Objects without persistence, and save them later with save method
|
12
|
+
|
13
|
+
=== 0.0.8 2016-02-04
|
14
|
+
* 3 minor enhancements:
|
15
|
+
* Add the draft invoices feature, refactoring invoices#create to create only draft invoices
|
16
|
+
* Add invoices#confirm to confirm draft invoices and insert them into the numbering sequence
|
17
|
+
* Add invoice_items#create for creating invoice items
|
18
|
+
|
19
|
+
=== 0.0.6 2015-10-13
|
20
|
+
* 2 minor enhancements:
|
21
|
+
* Add invoices#update for non already-sent invoices
|
22
|
+
* Add invoices#send to make the email sending programmable
|
23
|
+
|
24
|
+
=== 0.0.5 2015-07-21
|
25
|
+
* 1 minor enhancement:
|
26
|
+
* Add invoices#all customer filter and Customer.invoices method
|
27
|
+
|
28
|
+
=== 0.0.1 2015-02-06
|
29
|
+
|
30
|
+
* 1 major enhancement:
|
31
|
+
* Initial release
|
32
|
+
|
33
|
+
|
1
34
|
=== 0.0.12 2016-04-13
|
2
35
|
* 1 minor enhancement:
|
3
36
|
* Add pagination helpers
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
2.0.0
|
data/lib/octobat.rb
CHANGED
@@ -23,16 +23,32 @@ require 'octobat/api_resource'
|
|
23
23
|
require 'octobat/singleton_api_resource'
|
24
24
|
require 'octobat/list_object'
|
25
25
|
|
26
|
-
require 'octobat/numbering_sequence'
|
27
|
-
require 'octobat/payment_mode'
|
28
|
-
require 'octobat/payment'
|
29
|
-
require 'octobat/credit_note_numbering_sequence'
|
30
26
|
require 'octobat/customer'
|
27
|
+
require 'octobat/transaction'
|
28
|
+
require 'octobat/item'
|
29
|
+
require 'octobat/document'
|
31
30
|
require 'octobat/invoice'
|
32
|
-
require 'octobat/
|
31
|
+
require 'octobat/credit_note'
|
32
|
+
require 'octobat/payment_recipient'
|
33
|
+
require 'octobat/payment_recipient_reference'
|
34
|
+
require 'octobat/payment_source'
|
35
|
+
require 'octobat/invoice_numbering_sequence'
|
36
|
+
require 'octobat/credit_note_numbering_sequence'
|
37
|
+
require 'octobat/document_template'
|
38
|
+
require 'octobat/document_language'
|
39
|
+
require 'octobat/checkout'
|
40
|
+
require 'octobat/coupon'
|
41
|
+
require 'octobat/tax_region_setting'
|
42
|
+
require 'octobat/tax_evidence'
|
43
|
+
require 'octobat/tax_evidence_request'
|
44
|
+
require 'octobat/document_email_template'
|
45
|
+
require 'octobat/exports_setting'
|
46
|
+
require 'octobat/emails_setting'
|
47
|
+
|
33
48
|
|
34
49
|
# Errors
|
35
50
|
require 'octobat/errors/octobat_error'
|
51
|
+
require 'octobat/errors/octobat_lib_error'
|
36
52
|
require 'octobat/errors/api_error'
|
37
53
|
require 'octobat/errors/api_connection_error'
|
38
54
|
require 'octobat/errors/invalid_request_error'
|
@@ -40,8 +56,13 @@ require 'octobat/errors/authentication_error'
|
|
40
56
|
|
41
57
|
module Octobat
|
42
58
|
#DEFAULT_CA_BUNDLE_PATH = File.dirname(__FILE__) + '/data/ca-certificates.crt'
|
43
|
-
@api_base = 'https://
|
44
|
-
#@api_base = 'http://api.octobat.local:
|
59
|
+
@api_base = 'https://apiv2.octobat.com'
|
60
|
+
#@api_base = 'http://api.octobat.local:3052'
|
61
|
+
|
62
|
+
@max_network_retries = 0
|
63
|
+
@max_network_retry_delay = 2
|
64
|
+
@initial_network_retry_delay = 0.5
|
65
|
+
|
45
66
|
|
46
67
|
#@ssl_bundle_path = DEFAULT_CA_BUNDLE_PATH
|
47
68
|
#@verify_ssl_certs = true
|
@@ -50,6 +71,7 @@ module Octobat
|
|
50
71
|
|
51
72
|
class << self
|
52
73
|
attr_accessor :api_key, :api_base, :verify_ssl_certs, :api_version
|
74
|
+
attr_reader :max_network_retry_delay, :initial_network_retry_delay
|
53
75
|
end
|
54
76
|
|
55
77
|
def self.api_url(url='', api_base_url=nil)
|
@@ -98,19 +120,33 @@ module Octobat
|
|
98
120
|
end
|
99
121
|
end
|
100
122
|
|
101
|
-
request_opts.update(:headers => request_headers(api_key).update(headers),
|
123
|
+
request_opts.update(:headers => request_headers(api_key, method).update(headers),
|
102
124
|
:method => method, :open_timeout => 30,
|
103
125
|
:payload => payload, :url => url, :timeout => 80)
|
104
126
|
|
127
|
+
response = execute_request_with_rescues(request_opts, api_base_url)
|
128
|
+
[parse(response), api_key]
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.max_network_retries
|
132
|
+
@max_network_retries
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.max_network_retries=(val)
|
136
|
+
@max_network_retries = val.to_i
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
def self.execute_request_with_rescues(request_opts, api_base_url, retry_count = 0)
|
105
141
|
begin
|
106
142
|
response = execute_request(request_opts)
|
107
143
|
rescue SocketError => e
|
108
|
-
handle_restclient_error(e, api_base_url)
|
144
|
+
response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
|
109
145
|
rescue NoMethodError => e
|
110
146
|
# Work around RestClient bug
|
111
147
|
if e.message =~ /\WRequestFailed\W/
|
112
148
|
e = APIConnectionError.new('Unexpected HTTP response code')
|
113
|
-
handle_restclient_error(e, api_base_url)
|
149
|
+
response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
|
114
150
|
else
|
115
151
|
raise
|
116
152
|
end
|
@@ -118,13 +154,13 @@ module Octobat
|
|
118
154
|
if rcode = e.http_code and rbody = e.http_body
|
119
155
|
handle_api_error(rcode, rbody)
|
120
156
|
else
|
121
|
-
handle_restclient_error(e, api_base_url)
|
157
|
+
response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
|
122
158
|
end
|
123
159
|
rescue RestClient::Exception, Errno::ECONNREFUSED => e
|
124
|
-
handle_restclient_error(e, api_base_url)
|
160
|
+
response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
|
125
161
|
end
|
126
162
|
|
127
|
-
|
163
|
+
response
|
128
164
|
end
|
129
165
|
|
130
166
|
private
|
@@ -174,13 +210,17 @@ module Octobat
|
|
174
210
|
map { |k,v| "#{k}=#{Util.url_encode(v)}" }.join('&')
|
175
211
|
end
|
176
212
|
|
177
|
-
def self.request_headers(api_key)
|
213
|
+
def self.request_headers(api_key, method)
|
178
214
|
headers = {
|
179
215
|
:user_agent => "Octobat/v1 RubyBindings/#{Octobat::VERSION}",
|
180
216
|
:authorization => 'Basic ' + Base64.encode64( "#{api_key}:" ).chomp,
|
181
217
|
:content_type => 'application/x-www-form-urlencoded'
|
182
218
|
}
|
183
219
|
|
220
|
+
if [:post, :delete, :patch].include?(method) && self.max_network_retries > 0
|
221
|
+
headers[:idempotency_key] ||= SecureRandom.uuid
|
222
|
+
end
|
223
|
+
|
184
224
|
headers[:octobat_version] = api_version if api_version
|
185
225
|
|
186
226
|
begin
|
@@ -216,14 +256,14 @@ module Octobat
|
|
216
256
|
begin
|
217
257
|
error_obj = JSON.parse(rbody)
|
218
258
|
error_obj = Util.symbolize_names(error_obj)
|
219
|
-
error = error_obj[:
|
259
|
+
error = error_obj[:errors] or raise OctobatError.new # escape from parsing
|
220
260
|
|
221
261
|
rescue JSON::ParserError, OctobatError
|
222
262
|
raise general_api_error(rcode, rbody)
|
223
263
|
end
|
224
264
|
|
225
265
|
case rcode
|
226
|
-
when 400, 404
|
266
|
+
when 400, 402, 404, 422
|
227
267
|
raise invalid_request_error error, rcode, rbody, error_obj
|
228
268
|
when 401
|
229
269
|
raise authentication_error error, rcode, rbody, error_obj
|
@@ -234,8 +274,7 @@ module Octobat
|
|
234
274
|
end
|
235
275
|
|
236
276
|
def self.invalid_request_error(error, rcode, rbody, error_obj)
|
237
|
-
InvalidRequestError.new(error
|
238
|
-
rbody, error_obj)
|
277
|
+
InvalidRequestError.new(error, rcode, rbody, error_obj)
|
239
278
|
end
|
240
279
|
|
241
280
|
def self.authentication_error(error, rcode, rbody, error_obj)
|
@@ -251,7 +290,16 @@ module Octobat
|
|
251
290
|
APIError.new(error[:message], rcode, rbody, error_obj)
|
252
291
|
end
|
253
292
|
|
254
|
-
def self.handle_restclient_error(e, api_base_url=nil)
|
293
|
+
def self.handle_restclient_error(e, request_opts, retry_count, api_base_url=nil)
|
294
|
+
|
295
|
+
if should_retry?(e, retry_count)
|
296
|
+
retry_count = retry_count + 1
|
297
|
+
sleep sleep_time(retry_count)
|
298
|
+
response = execute_request_with_rescues(request_opts, api_base_url, retry_count)
|
299
|
+
return response
|
300
|
+
end
|
301
|
+
|
302
|
+
|
255
303
|
api_base_url = @api_base unless api_base_url
|
256
304
|
connection_message = "Please check your internet connection and try again. " \
|
257
305
|
"If this problem persists, you should check Octobat's service status at " \
|
@@ -282,6 +330,34 @@ module Octobat
|
|
282
330
|
|
283
331
|
end
|
284
332
|
|
333
|
+
if retry_count > 0
|
334
|
+
message += " Request was retried #{retry_count} times."
|
335
|
+
end
|
336
|
+
|
285
337
|
raise APIConnectionError.new(message + "\n\n(Network error: #{e.message})")
|
286
338
|
end
|
339
|
+
|
340
|
+
|
341
|
+
def self.should_retry?(e, retry_count)
|
342
|
+
puts "Retry count: #{retry_count}"
|
343
|
+
return false if retry_count >= self.max_network_retries
|
344
|
+
#return false if e.is_a?(RestClient::SSLCertificateNotVerified)
|
345
|
+
return true
|
346
|
+
end
|
347
|
+
|
348
|
+
def self.sleep_time(retry_count)
|
349
|
+
# This method was adapted from https://github.com/ooyala/retries/blob/master/lib/retries.rb
|
350
|
+
|
351
|
+
# The sleep time is an exponentially-increasing function of base_sleep_seconds. But, it never exceeds
|
352
|
+
# max_sleep_seconds.
|
353
|
+
sleep_seconds = [initial_network_retry_delay * (2 ** (retry_count - 1)), max_network_retry_delay].min
|
354
|
+
# Randomize to a random value in the range sleep_seconds/2 .. sleep_seconds
|
355
|
+
|
356
|
+
sleep_seconds = sleep_seconds * (0.5 * (1 + rand()))
|
357
|
+
# But never sleep less than base_sleep_seconds
|
358
|
+
sleep_seconds = [initial_network_retry_delay, sleep_seconds].max
|
359
|
+
|
360
|
+
sleep_seconds
|
361
|
+
end
|
362
|
+
|
287
363
|
end
|
@@ -2,23 +2,30 @@ module Octobat
|
|
2
2
|
module APIOperations
|
3
3
|
module List
|
4
4
|
def list(filters={}, opts={})
|
5
|
+
set_parent_resource(filters)
|
5
6
|
api_key, headers = Util.parse_opts(opts)
|
6
|
-
api_key ||= @api_key
|
7
|
-
|
8
|
-
#opts = Util.normalize_opts(opts)
|
9
|
-
#opts = @opts.merge(opts) if @opts
|
10
7
|
|
11
|
-
|
8
|
+
api_key ||= @api_key
|
9
|
+
|
10
|
+
f = filters.select{|request_filter| !@parent_resource.has_key?(request_filter)}
|
11
|
+
|
12
|
+
response, api_key = Octobat.request(:get, url, api_key, f, headers)
|
12
13
|
obj = ListObject.construct_from(response, api_key)
|
13
|
-
|
14
|
+
|
14
15
|
obj.filters = filters.dup
|
15
16
|
obj.cursors[:ending_before] = obj.filters.delete(:ending_before)
|
16
17
|
obj.cursors[:starting_after] = obj.filters.delete(:starting_after)
|
17
|
-
|
18
|
-
|
18
|
+
|
19
|
+
obj.filters.delete(:expand)
|
20
|
+
obj.parent_resource = @parent_resource
|
21
|
+
|
19
22
|
obj
|
20
23
|
end
|
21
|
-
|
24
|
+
|
25
|
+
def set_parent_resource(filters)
|
26
|
+
@parent_resource = {}
|
27
|
+
end
|
28
|
+
|
22
29
|
alias :all :list
|
23
30
|
end
|
24
31
|
end
|
@@ -1,8 +1,11 @@
|
|
1
1
|
module Octobat
|
2
2
|
module APIOperations
|
3
3
|
module Update
|
4
|
-
def save(opts={})
|
4
|
+
def save(opts={}, headers = {})
|
5
5
|
values = serialize_params(self).merge(opts)
|
6
|
+
|
7
|
+
api_key, headers = Util.parse_opts(headers)
|
8
|
+
api_key ||= @api_key
|
6
9
|
|
7
10
|
if @values[:metadata]
|
8
11
|
values[:metadata] = serialize_metadata
|
@@ -11,7 +14,7 @@ module Octobat
|
|
11
14
|
if values.length > 0
|
12
15
|
values.delete(:id)
|
13
16
|
|
14
|
-
response, api_key = Octobat.request(save_method, save_url,
|
17
|
+
response, api_key = Octobat.request(save_method, save_url, api_key, values)
|
15
18
|
refresh_from(response, api_key)
|
16
19
|
end
|
17
20
|
self
|
@@ -37,7 +40,7 @@ module Octobat
|
|
37
40
|
def serialize_params(obj)
|
38
41
|
case obj
|
39
42
|
when nil
|
40
|
-
''
|
43
|
+
self[:id].nil? ? nil : ''
|
41
44
|
when OctobatObject
|
42
45
|
unsaved_keys = obj.instance_variable_get(:@unsaved_values)
|
43
46
|
obj_values = obj.instance_variable_get(:@values)
|
@@ -53,7 +56,7 @@ module Octobat
|
|
53
56
|
end
|
54
57
|
end
|
55
58
|
|
56
|
-
|
59
|
+
protected
|
57
60
|
def save_url
|
58
61
|
if self[:id] == nil && self.class.respond_to?(:create)
|
59
62
|
self.class.url
|
data/lib/octobat/api_resource.rb
CHANGED
@@ -23,8 +23,11 @@ module Octobat
|
|
23
23
|
refresh_from(response, api_key)
|
24
24
|
end
|
25
25
|
|
26
|
-
def self.retrieve(id,
|
27
|
-
|
26
|
+
def self.retrieve(id, opts={})
|
27
|
+
api_key, headers = Util.parse_opts(opts)
|
28
|
+
opts[:api_key] ||= @api_key
|
29
|
+
|
30
|
+
instance = self.new(id, opts)
|
28
31
|
instance.refresh
|
29
32
|
instance
|
30
33
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Octobat
|
2
|
+
class Coupon < APIResource
|
3
|
+
extend Octobat::APIOperations::List
|
4
|
+
include Octobat::APIOperations::Create
|
5
|
+
include Octobat::APIOperations::Update
|
6
|
+
|
7
|
+
def activate
|
8
|
+
response, api_key = Octobat.request(:patch, activate_url, @api_key)
|
9
|
+
refresh_from(response, api_key)
|
10
|
+
end
|
11
|
+
|
12
|
+
def unactivate
|
13
|
+
response, api_key = Octobat.request(:patch, unactivate_url, @api_key)
|
14
|
+
refresh_from(response, api_key)
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete
|
18
|
+
response, api_key = Octobat.request(:delete, url, @api_key)
|
19
|
+
refresh_from(response, api_key)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def activate_url
|
25
|
+
url + '/activate'
|
26
|
+
end
|
27
|
+
|
28
|
+
def unactivate_url
|
29
|
+
url + '/unactivate'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|