quaderno 1.15.2 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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