quaderno 1.15.2 → 2.0.1

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.
data/changelog.md CHANGED
@@ -1,154 +1,183 @@
1
- #Changelog
2
- ##1.15.1
1
+ # Changelog
2
+
3
+ ## 2.0.1
4
+ * Fix an issue where requests would result in an error due to a missing `require`
5
+
6
+ ## 2.0.0
7
+ * **Breaking change** Pagination strategy has been updated to be cursor based. Collections no longer offer the `current_page` and `total_pages` methods, and offer `has_more?` and `next_page` instead. The `page` parameter is no longer supported either, in favour of the `created_before` parameter. [See more information in the documentation](https://developers.quaderno.io/api/#pagination).
8
+ * **Breaking change** `Quaderno::Tax.calculate` uses the [new tax calculation endpoint](https://developers.quaderno.io/api/#calculate-a-tax-rate). This endpoint accepts address parameters with the prefix `to_`. For example, `postal_code` is deprecated in favour of `to_postal_code`.
9
+ * `Quaderno::Tax.validate` uses the [new tax ID validation endpoint](https://developers.quaderno.io/api/#validate-a-tax-id). The signature did not change, and no updates are necessary.
10
+ * All requests now send the gem version in their User Agent header.
11
+
12
+ ## 1.17.1
13
+ * Do not modify the input arguments when the thread-safe mode is used.
14
+
15
+ ## 1.17.0
16
+ * Added `rate_limit_info` method to each API response
17
+ * **Breaking change:** `.delete` methods no longer returns a boolean but an instance of the removed object with a `deleted` attribute set to `true`
18
+ * **Breaking change:** `Quaderno::Base.ping` no longer returns a boolean but a `Quaderno::Base` instance with a `status` attribute.
19
+ * **Breaking change:** `Quaderno::Base.authorization` no longer returns a `Hash` but a `Quaderno::Base` instance with an `identity` attribute.
20
+ * **Breaking change:** `Quaderno::Base.me` no longer returns a `Hash` but a `Quaderno::Base` instance with all the attributes contained in the previous format.
21
+ * **Breaking change:** `Quaderno::Tax.validate_vat_number` no longer returns a boolean but a `Quaderno::Tax` instance with a `valid` attribute.
22
+ * **Breaking change:** `.deliver` no longer returns a `Hash` but a `Quaderno::Base` instance with a `success` attribute.
23
+ * **Breaking change:** Removed `Quaderno::Base.rate_limit_info`. Now it's an alias of `Quaderno::Base.ping`.
24
+
25
+ ## 1.16.0
26
+ * Added `Quaderno::CheckoutSession`
27
+
28
+ ## 1.15.2
29
+ * Relax `httparty` version requirement.
30
+
31
+ ## 1.15.1
3
32
  * Fix `Quaderno` load order.
4
33
 
5
- ##1.15.0
34
+ ## 1.15.0
6
35
  * Removed `jeweler` and updated the gem structure.
7
36
 
8
- ##1.14.0
37
+ ## 1.14.0
9
38
  * Added `domestic_taxes`, `sales_taxes`, `vat_moss`, `ec_sales` and `international_taxes` to `Quaderno::Report`
10
39
 
11
- ##1.13.2
40
+ ## 1.13.2
12
41
  * Added index method to `Quaderno::Tax` as `Quaderno::Tax.all()`.
13
42
 
14
- ##1.13.1
43
+ ## 1.13.1
15
44
  * Added `taxes` report to `Quaderno::Report`.
16
45
 
17
- ##1.13.0
46
+ ## 1.13.0
18
47
  * Added `Quaderno::Report`.
19
48
 
20
- ##1.12.5
49
+ ## 1.12.5
21
50
  * Added `create` method to `Quaderno::Income`.
22
51
  * Fix `Quaderno::Base.ping` and `Quaderno::Base.me` methods.
23
52
 
24
- ##1.12.4
53
+ ## 1.12.4
25
54
  * Use version headers on taxes requests.
26
55
 
27
- ##1.12.3
56
+ ## 1.12.3
28
57
  * Return integers insteado of strings on pagination readers.
29
58
 
30
- ##1.12.2
59
+ ## 1.12.2
31
60
  * Added `me` method.
32
61
 
33
- ##1.12.0
62
+ ## 1.12.0
34
63
  * Added thread-safe credentials configuration.
35
64
  * `all` methods returns a `Quaderno::Object` with pagination info instead of an `Array`
36
65
 
37
- ##1.11.2
66
+ ## 1.11.2
38
67
  * Specify `Content-Type` header so `HTTParty` don't merge the elements within the body content.
39
68
 
40
- ##1.11.1
69
+ ## 1.11.1
41
70
  * Added `retrieve` method for `Quaderno::Contact`, `Quaderno::Invoice`, `Quaderno::Credit`
42
71
  * Deprecate `retrieve_customer` as an alias of `retrieve`
43
72
 
44
- ##1.11.0
73
+ ## 1.11.0
45
74
  * Added `Quaderno::Tax.validate_vat_number` method
46
75
 
47
- ##1.10.0
76
+ ## 1.10.0
48
77
  * Added location evidences support
49
78
 
50
- ##1.9.2
79
+ ## 1.9.2
51
80
  * Added `Quaderno::Contact.retrieve` method
52
81
  * Code cleanup
53
82
  * Update tests
54
83
 
55
- ##1.9.1
84
+ ## 1.9.1
56
85
  * `Quaderno::Base.authorization` raises `Quaderno::Exceptions::InvalidSubdomainOrToken` instead returning false on wrong credentials
57
86
  * Inherit from `StandardError` instead of `Exception` for `Quaderno::Exceptions` by **@mvelikov**
58
87
 
59
- ##1.9.0
88
+ ## 1.9.0
60
89
  * Add support for new versioning system
61
90
 
62
- ##1.8.0
91
+ ## 1.8.0
63
92
  * Add Quaderno::Receipt support
64
93
  * Fix errors in README
65
94
 
66
- ##1.7.3
95
+ ## 1.7.3
67
96
  * Raise exception on failed updates
68
97
 
69
- ##1.7.2
70
- * Fix URL in `Quaderno::Tax.calculate` by [**@jcxplorer**] (https://github.com/jcxplorer)
98
+ ## 1.7.2
99
+ * Fix URL in `Quaderno::Tax.calculate` by [**@jcxplorer**](https://github.com/jcxplorer)
71
100
 
72
- ##1.7.1
101
+ ## 1.7.1
73
102
  * Breaking change: change configuration options
74
103
 
75
- ##1.7.0
104
+ ## 1.7.0
76
105
  * Added recurring documents
77
106
  * Raise existent exception
78
107
 
79
- ##1.6.1
108
+ ## 1.6.1
80
109
  * Fixed typo from old version released as 1.6.0
81
110
 
82
- ##1.6.0 (yanked)
111
+ ## 1.6.0 (yanked)
83
112
  * Crud module refactor
84
113
  * Added support for credit notes
85
114
 
86
- ##1.5.5
115
+ ## 1.5.5
87
116
  * Move rdoc as a development dependency
88
117
 
89
- ##1.5.4
118
+ ## 1.5.4
90
119
  * Remove transaction class.
91
120
 
92
- ##1.5.3
121
+ ## 1.5.3
93
122
  * Update `rate_limit_info` to fit the new rate limit
94
123
  * Remove transaction information from README (future resource name change)
95
124
  * Added new throttle limit exception (will be activated in future releases)
96
125
 
97
- ##1.5.1 and 1.5.2
126
+ ## 1.5.1 and 1.5.2
98
127
  * Remove debugger
99
128
  * `Quaderno::Exceptions::RequiredFieldsEmpty` replaced with `Quaderno::Exceptions::RequiredFieldsEmptyOrInvalid`
100
129
 
101
- ##1.5.0
130
+ ## 1.5.0
102
131
  * Added transactions
103
132
  * Raise `Quaderno::Exceptions::RequiredFieldsEmpty` for 422 responses
104
133
 
105
- ##1.4.2
134
+ ## 1.4.2
106
135
  * Find method hotfix
107
136
 
108
- ##1.4.1
137
+ ## 1.4.1
109
138
  * Fix wrong method name
110
139
  * Use correct organization in url
111
140
  * Add short description of the gem
112
141
 
113
- ##1.4.0
142
+ ## 1.4.0
114
143
  * Added taxes calculations support
115
144
  * Added webhooks documentation
116
145
 
117
- ##1.3.2
146
+ ## 1.3.2
118
147
 
119
148
  * Use new urls format
120
149
 
121
- ##1.3.1
150
+ ## 1.3.1
122
151
 
123
152
  * Added sandbox environment
124
153
  * Added `to_hash` instance method
125
154
 
126
- ##1.3.0
155
+ ## 1.3.0
127
156
 
128
157
  * Removed debug mode
129
158
 
130
- ##1.2.2
159
+ ## 1.2.2
131
160
 
132
161
  * Added ruby 2.0.0 compatibility
133
162
 
134
- ##1.2.1
163
+ ## 1.2.1
135
164
 
136
165
  * Deleted debugger dependency
137
166
 
138
- ##1.2.0
167
+ ## 1.2.0
139
168
 
140
169
  * Added Quaderno webhooks as a resource
141
170
  * Added authorization method
142
171
 
143
- ##1.1.2
172
+ ## 1.1.2
144
173
 
145
174
  * Fixed minor bugs
146
175
 
147
- ##1.1.0
176
+ ## 1.1.0
148
177
 
149
178
  * Added Quaderno items as a resource
150
179
  * Added filter in index queries
151
180
 
152
- ##1.0.0
181
+ ## 1.0.0
153
182
 
154
183
  * Initial release
data/lib/quaderno-ruby.rb CHANGED
@@ -3,9 +3,11 @@ end
3
3
 
4
4
  require 'ostruct'
5
5
 
6
+ require 'quaderno-ruby/version'
7
+ require 'quaderno-ruby/helpers/rate_limit'
6
8
  require 'quaderno-ruby/exceptions/exceptions'
7
9
  require 'quaderno-ruby/helpers/authentication'
8
10
  require 'quaderno-ruby/collection'
9
11
 
10
12
  %w(block crud deliver payment retrieve).each { |filename| require "quaderno-ruby/behavior/#{filename}" }
11
- %w(base contact item invoice receipt credit income estimate expense recurring document_item report evidence payment webhook tax).each { |filename| require "quaderno-ruby/#{ filename }" }
13
+ %w(base contact item invoice receipt credit income estimate expense recurring document_item report evidence payment webhook tax checkout_session).each { |filename| require "quaderno-ruby/#{ filename }" }
@@ -6,6 +6,7 @@ class Quaderno::Base < OpenStruct
6
6
  include Quaderno::Exceptions
7
7
  include Quaderno::Behavior::Crud
8
8
  include Quaderno::Helpers::Authentication
9
+ include Quaderno::Helpers::RateLimit
9
10
 
10
11
  PRODUCTION_URL = 'https://quadernoapp.com/api/'
11
12
  SANDBOX_URL = 'http://sandbox-quadernoapp.com/api/'
@@ -48,12 +49,15 @@ class Quaderno::Base < OpenStruct
48
49
  def self.authorization(auth_token, mode = nil)
49
50
  mode ||= :production
50
51
  url = mode == :sandbox ? SANDBOX_URL : PRODUCTION_URL
51
- response = get("#{url}authorization.json", basic_auth: { username: auth_token }, headers: version_header)
52
+ response = get("#{url}authorization.json", basic_auth: { username: auth_token }, headers: default_headers)
52
53
 
53
54
  if response.code == 200
54
- response.parsed_response
55
+ data = self.new(response.parsed_response)
56
+ data.rate_limit_info = response
57
+
58
+ data
55
59
  else
56
- raise(Quaderno::Exceptions::InvalidSubdomainOrToken, 'Invalid subdomain or token')
60
+ raise_exception(Quaderno::Exceptions::InvalidSubdomainOrToken, 'Invalid subdomain or token', response)
57
61
  end
58
62
  end
59
63
 
@@ -67,14 +71,21 @@ class Quaderno::Base < OpenStruct
67
71
 
68
72
  party_response = get("#{authentication[:url]}ping.json",
69
73
  basic_auth: authentication[:basic_auth],
70
- headers: version_header.merge(authentication[:headers])
74
+ headers: default_headers.merge(authentication[:headers])
71
75
  )
72
76
 
73
77
  check_exception_for(party_response, { subdomain_or_token: true })
74
78
  rescue Errno::ECONNREFUSED
75
- return false
79
+ return Quaderno::Base.new({ status: false })
76
80
  end
77
- true
81
+
82
+ data = self.new({ status: true })
83
+ data.rate_limit_info = party_response
84
+
85
+ data
86
+ end
87
+ class <<self
88
+ alias_method :rate_limit_info, :ping
78
89
  end
79
90
 
80
91
  def self.me(options = {})
@@ -85,19 +96,15 @@ class Quaderno::Base < OpenStruct
85
96
 
86
97
  party_response = get("#{authentication[:url]}me.json",
87
98
  basic_auth: authentication[:basic_auth],
88
- headers: version_header.merge(authentication[:headers])
99
+ headers: default_headers.merge(authentication[:headers])
89
100
  )
90
101
 
91
102
  check_exception_for(party_response, { subdomain_or_token: true })
92
103
 
93
- party_response.parsed_response
94
- end
104
+ data = self.new(party_response.parsed_response)
105
+ data.rate_limit_info = party_response
95
106
 
96
- #Returns the rate limit information: limit and remaining requests
97
- def self.rate_limit_info
98
- party_response = get("#{@@url}ping.json", basic_auth: { username: auth_token }, headers: version_header)
99
- check_exception_for(party_response, { subdomain_or_token: true })
100
- @@rate_limit_info = { reset: party_response.headers['x-ratelimit-reset'].to_i, remaining: party_response.headers["x-ratelimit-remaining"].to_i }
107
+ data
101
108
  end
102
109
 
103
110
  # Instance methods
@@ -128,7 +135,17 @@ class Quaderno::Base < OpenStruct
128
135
  @_document ||= document
129
136
  end
130
137
 
138
+ def self.default_headers
139
+ user_agent_header.merge(version_header)
140
+ end
141
+
142
+ def self.user_agent_header
143
+ { "User-Agent" => "Quaderno Ruby Gem #{Quaderno::VERSION}" }
144
+ end
145
+
131
146
  def self.version_header
132
147
  { 'Accept' => @@api_version.to_i.zero? ? "application/json" : "application/json; api_version=#{@@api_version.to_i}"}
133
148
  end
149
+
150
+ headers self.default_headers
134
151
  end
@@ -13,13 +13,16 @@ module Quaderno::Behavior
13
13
 
14
14
  response = put("#{authentication[:url]}#{api_model.api_path}/#{id}/block.json",
15
15
  basic_auth: authentication[:basic_auth],
16
- headers: version_header.merge(authentication[:headers])
16
+ headers: default_headers.merge(authentication[:headers])
17
17
  )
18
18
 
19
19
  check_exception_for(response, { rate_limit: true, subdomain_or_token: true, id: true })
20
20
  doc = response.parsed_response
21
21
 
22
- new doc
22
+ object = new doc
23
+ object.rate_limit_info = response
24
+
25
+ object
23
26
  end
24
27
  end
25
28
  end
@@ -23,34 +23,28 @@ module Quaderno::Behavior
23
23
  element
24
24
  end
25
25
 
26
+ def all_from_url(url, options = {})
27
+ authentication = get_authentication(api_model: api_model)
28
+
29
+ response = get(url,
30
+ basic_auth: authentication[:basic_auth],
31
+ headers: default_headers.merge(authentication[:headers])
32
+ )
33
+
34
+ handle_all_response(response, authentication, options)
35
+ end
36
+
26
37
  def all(options = {})
27
38
  authentication = get_authentication(options.merge(api_model: api_model))
28
- filter = options.delete_if { |k,v| %w(auth_token access_token api_url mode api_model).include? k.to_s }
39
+ filter = options.dup.delete_if { |k,v| %w(auth_token access_token api_url mode api_model).include? k.to_s }
29
40
 
30
41
  response = get("#{authentication[:url]}#{api_model.api_path}.json",
31
42
  query: filter,
32
43
  basic_auth: authentication[:basic_auth],
33
- headers: version_header.merge(authentication[:headers])
44
+ headers: default_headers.merge(authentication[:headers])
34
45
  )
35
46
 
36
- check_exception_for(response, { rate_limit: true, subdomain_or_token: true })
37
- array = response.parsed_response
38
- collection = Quaderno::Collection.new
39
-
40
- if is_a_document?
41
- array.each do |element|
42
- element[:authentication_data] = authentication
43
- api_model.parse_nested(element)
44
- collection << (new element)
45
- end
46
- else
47
- array.each { |element| collection << (new element) }
48
- end
49
-
50
- collection.current_page = response.headers['x-pages-currentpage']
51
- collection.total_pages = response.headers['x-pages-totalpages']
52
-
53
- collection
47
+ handle_all_response(response, authentication, options)
54
48
  end
55
49
 
56
50
  def find(id, options = {})
@@ -58,7 +52,7 @@ module Quaderno::Behavior
58
52
 
59
53
  response = get("#{authentication[:url]}#{api_model.api_path}/#{id}.json",
60
54
  basic_auth: authentication[:basic_auth],
61
- headers: version_header.merge(authentication[:headers])
55
+ headers: default_headers.merge(authentication[:headers])
62
56
  )
63
57
 
64
58
  check_exception_for(response, { rate_limit: true, subdomain_or_token: true, id: true })
@@ -67,17 +61,20 @@ module Quaderno::Behavior
67
61
 
68
62
  api_model.parse_nested(hash) if is_a_document?
69
63
 
70
- new hash
64
+ object = new hash
65
+ object.rate_limit_info = response
66
+
67
+ object
71
68
  end
72
69
 
73
70
  def create(params = {})
74
71
  authentication = get_authentication(params.merge(api_model: api_model))
75
- params.delete_if { |k,v| %w(auth_token access_token api_url mode api_model').include? k.to_s }
72
+ params.dup.delete_if { |k,v| %w(auth_token access_token api_url mode api_model').include? k.to_s }
76
73
 
77
74
  response = post("#{authentication[:url]}#{api_model.api_path}.json",
78
75
  body: params.to_json,
79
76
  basic_auth: authentication[:basic_auth],
80
- headers: version_header.merge(authentication[:headers]).merge('Content-Type' => 'application/json')
77
+ headers: default_headers.merge(authentication[:headers]).merge('Content-Type' => 'application/json')
81
78
  )
82
79
 
83
80
  check_exception_for(response, { rate_limit: true, subdomain_or_token: true, required_fields: true })
@@ -86,17 +83,20 @@ module Quaderno::Behavior
86
83
 
87
84
  api_model.parse_nested(hash) if is_a_document?
88
85
 
89
- new hash
86
+ object = new hash
87
+ object.rate_limit_info = response
88
+
89
+ object
90
90
  end
91
91
 
92
92
  def update(id, params = {})
93
93
  authentication = get_authentication(params.merge(api_model: api_model))
94
- params = params.delete_if { |k,v| %w(auth_token access_token api_url mode api_model').include? k.to_s }
94
+ params = params.dup.delete_if { |k,v| %w(auth_token access_token api_url mode api_model').include? k.to_s }
95
95
 
96
96
  response = put("#{authentication[:url]}#{api_model.api_path}/#{id}.json",
97
97
  body: params.to_json,
98
98
  basic_auth: authentication[:basic_auth],
99
- headers: version_header.merge(authentication[:headers]).merge('Content-Type' => 'application/json')
99
+ headers: default_headers.merge(authentication[:headers]).merge('Content-Type' => 'application/json')
100
100
  )
101
101
 
102
102
  check_exception_for(response, { rate_limit: true, required_fields: true, subdomain_or_token: true, id: true })
@@ -105,7 +105,10 @@ module Quaderno::Behavior
105
105
 
106
106
  api_model.parse_nested(hash) if is_a_document?
107
107
 
108
- new hash
108
+ object = new hash
109
+ object.rate_limit_info = response
110
+
111
+ object
109
112
  end
110
113
 
111
114
  def delete(id, options = {})
@@ -113,11 +116,42 @@ module Quaderno::Behavior
113
116
 
114
117
  response = HTTParty.delete("#{authentication[:url]}#{ api_model.api_path }/#{ id }.json",
115
118
  basic_auth: authentication[:basic_auth],
116
- headers: version_header.merge(authentication[:headers])
119
+ headers: default_headers.merge(authentication[:headers])
117
120
  )
118
121
  check_exception_for(response, { rate_limit: true, subdomain_or_token: true, id: true, has_documents: true })
119
122
 
120
- true
123
+ hash = { deleted: true, id: id }
124
+
125
+ object = new hash
126
+ object.rate_limit_info = response
127
+
128
+ object
129
+ end
130
+
131
+ private
132
+
133
+ def handle_all_response(response, authentication, request_options)
134
+ check_exception_for(response, { rate_limit: true, subdomain_or_token: true })
135
+ array = response.parsed_response
136
+ collection = Quaderno::Collection.new
137
+
138
+ if is_a_document?
139
+ array.each do |element|
140
+ element[:authentication_data] = authentication
141
+ api_model.parse_nested(element)
142
+ collection << (new element)
143
+ end
144
+ else
145
+ array.each { |element| collection << (new element) }
146
+ end
147
+
148
+ collection.rate_limit_info = response
149
+ collection.request_options = request_options
150
+ collection.collection_type = self
151
+ collection.has_more = response.headers['x-pages-hasmore']
152
+ collection.next_page_url = response.headers['x-pages-nextpage']
153
+
154
+ collection
121
155
  end
122
156
  end
123
157
  end