bunq-client 0.7.2 → 1.0.0

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