bunq-client 0.7.2 → 1.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +40 -0
  3. data/.rubocop.yml +212 -0
  4. data/.ruby-version +1 -1
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +2 -0
  7. data/README.md +2 -0
  8. data/Rakefile +7 -3
  9. data/bunq-client.gemspec +22 -20
  10. data/lib/bunq/attachment.rb +15 -0
  11. data/lib/bunq/attachment_public.rb +13 -0
  12. data/lib/bunq/attachment_public_content.rb +4 -2
  13. data/lib/bunq/attachment_publics.rb +20 -0
  14. data/lib/bunq/attachments.rb +20 -0
  15. data/lib/bunq/avatar.rb +15 -0
  16. data/lib/bunq/avatars.rb +15 -0
  17. data/lib/bunq/bunq.rb +2 -0
  18. data/lib/bunq/bunqme_tab.rb +17 -0
  19. data/lib/bunq/bunqme_tabs.rb +21 -0
  20. data/lib/bunq/card.rb +2 -0
  21. data/lib/bunq/cards.rb +3 -1
  22. data/lib/bunq/certificate_pinned.rb +10 -6
  23. data/lib/bunq/client.rb +74 -51
  24. data/lib/bunq/device_servers.rb +5 -4
  25. data/lib/bunq/draft_share_invite_bank.rb +2 -0
  26. data/lib/bunq/draft_share_invite_banks.rb +3 -1
  27. data/lib/bunq/encryptor.rb +6 -7
  28. data/lib/bunq/errors.rb +9 -2
  29. data/lib/bunq/header.rb +20 -0
  30. data/lib/bunq/installation.rb +2 -0
  31. data/lib/bunq/installations.rb +4 -2
  32. data/lib/bunq/monetary_account.rb +17 -0
  33. data/lib/bunq/monetary_account_bank.rb +17 -0
  34. data/lib/bunq/monetary_account_banks.rb +21 -0
  35. data/lib/bunq/monetary_accounts.rb +3 -1
  36. data/lib/bunq/notification_filter_url.rb +4 -2
  37. data/lib/bunq/paginated.rb +14 -3
  38. data/lib/bunq/payment.rb +2 -0
  39. data/lib/bunq/payments.rb +3 -1
  40. data/lib/bunq/qr_code_content.rb +5 -3
  41. data/lib/bunq/resource.rb +25 -11
  42. data/lib/bunq/session_servers.rb +5 -2
  43. data/lib/bunq/signature.rb +9 -7
  44. data/lib/bunq/user.rb +17 -0
  45. data/lib/bunq/user_company.rb +2 -0
  46. data/lib/bunq/user_person.rb +2 -0
  47. data/lib/bunq/users.rb +17 -0
  48. data/lib/bunq/version.rb +3 -1
  49. metadata +53 -20
  50. data/.travis.yml +0 -5
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module Bunq
6
+ class Attachments
7
+ def initialize(parent_resource)
8
+ @resource = parent_resource.append('/attachment')
9
+ end
10
+
11
+ def create(binary_payload, description, mime_type)
12
+ custom_headers = {
13
+ Bunq::Header::CONTENT_TYPE => mime_type,
14
+ Bunq::Header::ATTACHMENT_DESCRIPTION => description,
15
+ }
16
+ payload = Base64.decode64(binary_payload)
17
+ @resource.with_session { @resource.post(payload, custom_headers: custom_headers) }['Response']
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'resource'
4
+
5
+ module Bunq
6
+ class Avatar
7
+ def initialize(client, id)
8
+ @resource = Bunq::Resource.new(client, "/v1/avatar/#{id}")
9
+ end
10
+
11
+ def show
12
+ @resource.with_session { @resource.get }['Response']
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'resource'
4
+
5
+ module Bunq
6
+ class Avatars
7
+ def initialize(client)
8
+ @resource = Bunq::Resource.new(client, '/v1/avatar')
9
+ end
10
+
11
+ def create(attachment_public_uuid)
12
+ @resource.with_session { @resource.post({attachment_public_uuid: attachment_public_uuid}) }['Response']
13
+ end
14
+ end
15
+ end
data/lib/bunq/bunq.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rest-client'
2
4
  require_relative './client'
3
5
  require_relative './signature'
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bunq
4
+ class BunqmeTab
5
+ def initialize(parent_resource, id)
6
+ @resource = parent_resource.append("/bunqme-tab/#{id}")
7
+ end
8
+
9
+ def show
10
+ @resource.with_session { @resource.get }['Response']
11
+ end
12
+
13
+ def update(attributes)
14
+ @resource.with_session { @resource.put(attributes) }['Response']
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'paginated'
4
+
5
+ module Bunq
6
+ class BunqmeTabs
7
+ def initialize(parent_resource)
8
+ @resource = parent_resource.append('/bunqme-tab')
9
+ end
10
+
11
+ def index(count: 200, older_id: nil, newer_id: nil)
12
+ Bunq::Paginated
13
+ .new(@resource)
14
+ .paginate(count: count, older_id: older_id, newer_id: newer_id)
15
+ end
16
+
17
+ def create(attributes)
18
+ @resource.with_session { @resource.post(attributes) }['Response']
19
+ end
20
+ end
21
+ end
data/lib/bunq/card.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  class Card
3
5
  def initialize(parent_resource, id)
data/lib/bunq/cards.rb CHANGED
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  class Cards
3
5
  def initialize(parent_resource)
4
- @resource = parent_resource.append("/card")
6
+ @resource = parent_resource.append('/card')
5
7
  end
6
8
 
7
9
  def index
@@ -1,20 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  ##
3
5
  # https://doc.bunq.com/api/1/call/certificate-pinned
4
6
  class CertificatePinned
5
7
  def initialize(parent_resource)
6
- @resource = parent_resource.append("/certificate-pinned")
8
+ @resource = parent_resource.append('/certificate-pinned')
7
9
  end
8
10
 
9
11
  ##
10
12
  # https://doc.bunq.com/api/1/call/certificate-pinned/method/post
11
13
  def create(pem_certificate)
12
14
  @resource.with_session do
13
- @resource.post({
14
- certificate_chain: [
15
- {certificate: pem_certificate}
16
- ]
17
- })
15
+ @resource.post(
16
+ {
17
+ certificate_chain: [
18
+ {certificate: pem_certificate},
19
+ ],
20
+ },
21
+ )
18
22
  end
19
23
  end
20
24
  end
data/lib/bunq/client.rb CHANGED
@@ -1,10 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
2
4
  require 'base64'
3
5
  require 'thread_safe'
4
6
 
5
7
  require_relative './version'
8
+ require_relative './header'
6
9
  require_relative './resource'
7
10
 
11
+ require_relative './attachment_publics'
12
+ require_relative './attachment_public'
13
+ require_relative './avatars'
14
+ require_relative './avatar'
8
15
  require_relative './installations'
9
16
  require_relative './installation'
10
17
  require_relative './device_servers'
@@ -18,7 +25,7 @@ require_relative './monetary_accounts'
18
25
  require_relative './payment'
19
26
  require_relative './payments'
20
27
  require_relative './signature'
21
- require_relative './attachment_public_content.rb'
28
+ require_relative './attachment_public_content'
22
29
 
23
30
  ##
24
31
  # Usage
@@ -53,7 +60,8 @@ module Bunq
53
60
  # Returns a new instance of +Client+ with the current +configuration+.
54
61
  #
55
62
  def client
56
- fail "No configuration! Call Bunq.configure first." unless configuration
63
+ fail 'No configuration! Call Bunq.configure first.' unless configuration
64
+
57
65
  Client.new(configuration.dup)
58
66
  end
59
67
  end
@@ -112,40 +120,40 @@ module Bunq
112
120
 
113
121
  # Base url for the bunq api. Defaults to +PRODUCTION_BASE_URL+
114
122
  attr_accessor :base_url,
115
- # Flag to set to connect to sandbox. Defaults to +false+.
116
- # If set to +true+ you must also specify +sandbox_user+
117
- # and +sandbox_password+
118
- :sandbox,
119
- # The username for connecting to the sandbox
120
- :sandbox_user,
121
- # The password for connecting to the sandbox
122
- :sandbox_password,
123
- # Your installation token obtained from bunq
124
- :installation_token,
125
- # Your api key obtained from bunq
126
- :api_key,
127
- # Your language. Defaults to +DEFAULT_LANGUAGE+
128
- :language,
129
- # Your region. Defaults to +DEFAULT_REGION+
130
- :region,
131
- # Your geolocation. Defaults to +DEFAULT_GEOLOCATION+
132
- :geolocation,
133
- # Arbitrary user agent to connect to bunq. Defaults to +DEFAULT_USER_AGENT+
134
- :user_agent,
135
- # Flag to set when you want to disable the signature
136
- # retrieved from bunq. Mainly useful for testing.
137
- # Defaults to +false+
138
- :disable_response_signature_verification,
139
- # The private key for signing the request
140
- :private_key,
141
- # The public key of this installation for verifying the response
142
- :server_public_key,
143
- # Timeout in seconds to wait for bunq api. Defaults to +DEFAULT_TIMEOUT+
144
- :timeout,
145
- # Cache to retrieve current session from. Defaults to +DEFAULT_SESSION_CACHE+,
146
- # which will create a new session per `Bunq.client` instance.
147
- # See +ThreadSafeSessionCache+ for more advanced use.
148
- :session_cache
123
+ # Flag to set to connect to sandbox. Defaults to +false+.
124
+ # If set to +true+ you must also specify +sandbox_user+
125
+ # and +sandbox_password+
126
+ :sandbox,
127
+ # The username for connecting to the sandbox
128
+ :sandbox_user,
129
+ # The password for connecting to the sandbox
130
+ :sandbox_password,
131
+ # Your installation token obtained from bunq
132
+ :installation_token,
133
+ # Your api key obtained from bunq
134
+ :api_key,
135
+ # Your language. Defaults to +DEFAULT_LANGUAGE+
136
+ :language,
137
+ # Your region. Defaults to +DEFAULT_REGION+
138
+ :region,
139
+ # Your geolocation. Defaults to +DEFAULT_GEOLOCATION+
140
+ :geolocation,
141
+ # Arbitrary user agent to connect to bunq. Defaults to +DEFAULT_USER_AGENT+
142
+ :user_agent,
143
+ # Flag to set when you want to disable the signature
144
+ # retrieved from bunq. Mainly useful for testing.
145
+ # Defaults to +false+
146
+ :disable_response_signature_verification,
147
+ # The private key for signing the request
148
+ :private_key,
149
+ # The public key of this installation for verifying the response
150
+ :server_public_key,
151
+ # Timeout in seconds to wait for bunq api. Defaults to +DEFAULT_TIMEOUT+
152
+ :timeout,
153
+ # Cache to retrieve current session from. Defaults to +DEFAULT_SESSION_CACHE+,
154
+ # which will create a new session per `Bunq.client` instance.
155
+ # See +ThreadSafeSessionCache+ for more advanced use.
156
+ :session_cache
149
157
 
150
158
  def initialize
151
159
  @sandbox = false
@@ -165,14 +173,33 @@ module Bunq
165
173
  #
166
174
  # An instance of a +Client+ can be obtained via +Bunq.client+
167
175
  class Client
176
+ APPLICATION_JSON = 'application/json'
177
+
168
178
  attr_accessor :current_session
169
179
  attr_reader :configuration
170
180
 
171
181
  def initialize(configuration)
172
- fail ArgumentError.new('configuration is required') unless configuration
182
+ fail ArgumentError, 'configuration is required' unless configuration
183
+
173
184
  @configuration = configuration
174
185
  end
175
186
 
187
+ def attachment_publics
188
+ Bunq::AttachmentPublics.new(self)
189
+ end
190
+
191
+ def attachment_public(id)
192
+ Bunq::AttachmentPublic.new(self, id)
193
+ end
194
+
195
+ def avatars
196
+ Bunq::Avatars.new(self)
197
+ end
198
+
199
+ def avatar(id)
200
+ Bunq::Avatar.new(self, id)
201
+ end
202
+
176
203
  def installations
177
204
  Bunq::Installations.new(self)
178
205
  end
@@ -226,7 +253,7 @@ module Bunq
226
253
  end
227
254
 
228
255
  def ensure_session!
229
- @current_session ||= configuration.session_cache.get { create_session }
256
+ @current_session ||= configuration.session_cache.get { create_session } # rubocop:disable Naming/MemoizedInstanceVariableName
230
257
  end
231
258
 
232
259
  def create_session
@@ -254,21 +281,17 @@ module Bunq
254
281
 
255
282
  def headers
256
283
  {
257
- 'Accept': 'application/json',
258
- 'Cache-Control': 'no-cache',
259
- 'Content-Type': 'application/json',
260
- 'User-Agent': configuration.user_agent,
261
- 'X-Bunq-Language': configuration.language,
262
- 'X-Bunq-Geolocation': configuration.geolocation,
263
- 'X-Bunq-Region': configuration.region,
284
+ Bunq::Header::ACCEPT => APPLICATION_JSON,
285
+ Bunq::Header::CACHE_CONTROL => 'no-cache',
286
+ Bunq::Header::CONTENT_TYPE => APPLICATION_JSON,
287
+ Bunq::Header::USER_AGENT => configuration.user_agent,
288
+ Bunq::Header::LANGUAGE => configuration.language,
289
+ Bunq::Header::GEOLOCATION => configuration.geolocation,
290
+ Bunq::Header::REGION => configuration.region,
264
291
  }.tap do |h|
265
- if configuration.installation_token
266
- h[:'X-Bunq-Client-Authentication'] = configuration.installation_token
267
- end
292
+ h[Bunq::Header::CLIENT_AUTH] = configuration.installation_token if configuration.installation_token
268
293
 
269
- if current_session
270
- h[:'X-Bunq-Client-Authentication'] = current_session[1]['Token']['token']
271
- end
294
+ h[Bunq::Header::CLIENT_AUTH] = current_session[1]['Token']['token'] if current_session
272
295
  end
273
296
  end
274
297
 
@@ -1,13 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  ##
3
5
  # See https://doc.bunq.com/api/1/call/device-server
4
6
  class DeviceServers
5
-
6
7
  ##
7
8
  # +client+ an instance of +Bunq::Client+
8
9
  #
9
10
  def initialize(client)
10
- @resource = Bunq::Resource.new(client, "/v1/device-server")
11
+ @resource = Bunq::Resource.new(client, '/v1/device-server')
11
12
  @client = client
12
13
  end
13
14
 
@@ -20,10 +21,10 @@ module Bunq
20
21
  # @param description [String] The description of this device server.
21
22
  # @param permitted_ips [Array|nil] Array of permitted IP addresses.
22
23
  def create(description, permitted_ips: nil)
23
- fail ArgumentError.new('description is required') unless description
24
+ fail ArgumentError, 'description is required' unless description
24
25
  fail 'Cannot create session, please add the api_key to your configuration' unless @client.configuration.api_key
25
26
 
26
- params = { description: description, secret: @client.configuration.api_key }
27
+ params = {description: description, secret: @client.configuration.api_key}
27
28
  params[:permitted_ips] = permitted_ips if permitted_ips
28
29
 
29
30
  @resource.post(params)['Response']
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'qr_code_content'
2
4
 
3
5
  module Bunq
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'qr_code_content'
2
4
 
3
5
  module Bunq
4
6
  class DraftShareInviteBanks
5
7
  def initialize(parent_resource)
6
- @resource = parent_resource.append("/draft-share-invite-bank")
8
+ @resource = parent_resource.append('/draft-share-invite-bank')
7
9
  end
8
10
 
9
11
  def create(invite)
@@ -1,13 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  class Encryptor
3
- HEADER_CLIENT_ENCRYPTION_HMAC = 'X-Bunq-Client-Encryption-Hmac'
4
- HEADER_CLIENT_ENCRYPTION_IV = 'X-Bunq-Client-Encryption-Iv'
5
- HEADER_CLIENT_ENCRYPTION_KEY = 'X-Bunq-Client-Encryption-Key'
6
5
  AES_ENCRYPTION_METHOD = 'aes-256-cbc'
7
6
  HMAC_ALGORITHM = 'sha1'
8
7
 
9
8
  def initialize(server_public_key)
10
- fail ArgumentError.new('server_public_key is mandatory') unless server_public_key
9
+ fail ArgumentError, 'server_public_key is mandatory' unless server_public_key
11
10
 
12
11
  @server_public_key = OpenSSL::PKey::RSA.new(server_public_key)
13
12
  end
@@ -17,13 +16,13 @@ module Bunq
17
16
 
18
17
  iv, key, encrypted_body = encrypt_body(body)
19
18
 
20
- headers[HEADER_CLIENT_ENCRYPTION_IV] = Base64.strict_encode64(iv)
19
+ headers[Bunq::Header::CLIENT_ENCRYPTION_IV] = Base64.strict_encode64(iv)
21
20
 
22
21
  encrypted_key = server_public_key.public_encrypt(key)
23
- headers[HEADER_CLIENT_ENCRYPTION_KEY] = Base64.strict_encode64(encrypted_key)
22
+ headers[Bunq::Header::CLIENT_ENCRYPTION_KEY] = Base64.strict_encode64(encrypted_key)
24
23
 
25
24
  digest = hmac(key, iv + encrypted_body)
26
- headers[HEADER_CLIENT_ENCRYPTION_HMAC] = Base64.strict_encode64(digest)
25
+ headers[Bunq::Header::CLIENT_ENCRYPTION_HMAC] = Base64.strict_encode64(digest)
27
26
 
28
27
  [encrypted_body, headers]
29
28
  end
data/lib/bunq/errors.rb CHANGED
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Bunq
2
4
  class ResponseError < StandardError
3
5
  attr_reader :code
4
6
  attr_reader :headers
5
7
  attr_reader :body
6
8
 
7
- def initialize(msg = "Response error", code: nil, headers: nil, body: nil)
9
+ def initialize(msg = 'Response error', code: nil, headers: nil, body: nil)
8
10
  @code = code
9
11
  @headers = headers || {}
10
12
  @body = body
@@ -14,7 +16,12 @@ module Bunq
14
16
  # Returns the parsed body if it is a JSON document, nil otherwise.
15
17
  # @param opts [Hash] Optional options that are passed to `JSON.parse`.
16
18
  def parsed_body(opts = {})
17
- JSON.parse(@body, opts) if @body && @headers['content-type'] && @headers['content-type'].include?('application/json')
19
+ if @body && @headers['content-type'] && @headers['content-type'].include?('application/json')
20
+ JSON.parse(
21
+ @body,
22
+ opts,
23
+ )
24
+ end
18
25
  end
19
26
 
20
27
  # Returns an array of errors returned from the API, or nil if no errors are returned.