omisego 0.9.4

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/README.md ADDED
@@ -0,0 +1,447 @@
1
+ # OmiseGO
2
+
3
+ OmiseGO is a Ruby SDK meant to communicate with an OmiseGO eWallet setup.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```
10
+ gem 'omisego'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install omisego
20
+
21
+ ## Initialization
22
+
23
+ The OmiseGO SDK can either be initialized on a global level, or a on client basis. However, initializing on a global level is not necessarily what you want and won't be thread-safe. If using Rails, Sticking a `client` method in your `ApplicationController` will probably be a better solution than using an initializer as shown below.
24
+
25
+ In the end, the choice is yours and the optimal solution depends on your needs.
26
+
27
+ ### Global init
28
+
29
+ ```
30
+ # config/initializers/omisego.rb
31
+ OmiseGO.configure do |config|
32
+ config.access_key = ENV['OMISEGO_ACCESS_KEY']
33
+ config.secret_key = ENV['OMISEGO_SECRET_KEY']
34
+ config.base_url = ENV['OMISEGO_BASE_URL']
35
+ end
36
+ ```
37
+
38
+ If initialized this way, the `OmiseGO` classes can be used without specifying the client.
39
+
40
+ ```
41
+ user = OmiseGO::User.find(provider_user_id: 'some_uuid')
42
+ ```
43
+
44
+ ### Logging
45
+
46
+ The Ruby SDK comes with the possibility to log requests to the eWallet. For example, within a Rails application, the following can be defined:
47
+
48
+ ```
49
+ # config/initializers/omisego.rb
50
+ OmiseGO.configure do |config|
51
+ config.access_key = ENV['OMISEGO_ACCESS_KEY']
52
+ config.secret_key = ENV['OMISEGO_SECRET_KEY']
53
+ config.base_url = ENV['OMISEGO_BASE_URL']
54
+ config.logger = Rails.logger
55
+ end
56
+ ```
57
+
58
+ This would provide the following in the logs:
59
+
60
+ ```
61
+ [OmiseGO] Request: POST login
62
+ User-Agent: Faraday v0.13.1
63
+ Authorization: [FILTERED]
64
+ Accept: application/vnd.omisego.v1+json
65
+ Content-Type: application/vnd.omisego.v1+json
66
+
67
+ {"provider_user_id":"aeab0d51-b3d9-415d-98ef-f9162903f024"}
68
+
69
+ [OmiseGO] Response: HTTP/200
70
+ Connection: close
71
+ Server: Cowboy
72
+ Date: Wed, 14 Feb 2018 04:35:52 GMT
73
+ Content-Length: 140
74
+ Content-Type: application/vnd.omisego.v1+json; charset=utf-8
75
+ Cache-Control: max-age=0, private, must-revalidate
76
+
77
+ {"version":"1","success":true,"data":{"object":"authentication_token","authentication_token":[FILTERED]}}
78
+ ```
79
+
80
+ ### Client init
81
+
82
+ With this approach, the client needs to be passed in every call and will be used as the call initiator.
83
+
84
+ ```
85
+ client = OmiseGO::Client.new(
86
+ access_key: ENV['OMISEGO_ACCESS_KEY'],
87
+ secret_key: ENV['OMISEGO_SECRET_KEY'],
88
+ base_url: ENV['OMISEGO_BASE_URL']
89
+ )
90
+
91
+ user = OmiseGO::User.find(provider_user_id: 'some_uuid', client: client)
92
+ ```
93
+
94
+ ## Usage
95
+
96
+ All the calls below will communicate with the OmiseGO wallet specified in the `base_url` configuration. They will either return an instance of `OmiseGO:Error` or of the appropriate model (`User`, `Balance`, etc.), see [the list of models](#models) for more information.
97
+
98
+ __The method `#error?` can be used on any model to check if it's an error or a valid result.__
99
+
100
+ ### Managing Users
101
+
102
+ #### Find
103
+
104
+ Retrieve a user from the eWallet API.
105
+
106
+ ```
107
+ user = OmiseGO::User.find(
108
+ provider_user_id: 'some_uuid'
109
+ )
110
+ ```
111
+
112
+ Returns either:
113
+ - An `OmiseGO::User` instance
114
+ - An `OmiseGO::Error` instance
115
+
116
+ #### Create
117
+
118
+ Create a user in the eWallet API database. The `provider_user_id` is how a user is identified and cannot be changed later on.
119
+
120
+ ```
121
+ user = OmiseGO::User.create(
122
+ provider_user_id: 'some_uuid',
123
+ username: 'john@doe.com',
124
+ metadata: {
125
+ first_name: 'John',
126
+ last_name: 'Doe'
127
+ }
128
+ )
129
+ ```
130
+
131
+ Returns either:
132
+ - An `OmiseGO::User` instance
133
+ - An `OmiseGO::Error` instance
134
+
135
+ #### Update
136
+
137
+ Update a user in the eWallet API database. All fields need to be provided and the values in the eWallet database will be replaced with the sent ones (behaves like a HTTP `PUT`). Sending `metadata: {}` in the request below would remove the `first_name` and `last_name` fields for example.
138
+
139
+ ```
140
+ user = OmiseGO::User.update(
141
+ provider_user_id: 'some_uuid',
142
+ username: 'jane@doe.com',
143
+ metadata: {
144
+ first_name: 'Jane',
145
+ last_name: 'Doe'
146
+ }
147
+ )
148
+ ```
149
+
150
+ Returns either:
151
+ - An `OmiseGO::User` instance
152
+ - An `OmiseGO::Error` instance
153
+
154
+ ### Managing Sessions
155
+
156
+ #### Login
157
+
158
+ Login a user and retrieve an `authentication_token` that can be passed to a mobile client to make calls to the eWallet API directly.
159
+
160
+ ```
161
+ auth_token = OmiseGO::User.login(
162
+ provider_user_id: 'some_uuid'
163
+ )
164
+ ```
165
+
166
+ Returns either:
167
+ - An `OmiseGO::AuthenticationToken` instance
168
+ - An `OmiseGO::Error` instance
169
+
170
+ ### Managing Balances
171
+
172
+ - [All](#All)
173
+ - [Credit](#Credit)
174
+ - [Debit](#Debit)
175
+
176
+ #### All
177
+
178
+ Retrieve a list of addresses (with only one address for now) containing a list of balances.
179
+
180
+ ```
181
+ address = OmiseGO::Balance.all(
182
+ provider_user_id: 'some_uuid'
183
+ )
184
+ ```
185
+
186
+ Returns either:
187
+ - An `OmiseGO::Address` instance
188
+ - An `OmiseGO::Error` instance
189
+
190
+ #### Credit
191
+
192
+ Transfer the specified amount (as an integer, down to the `subunit_to_unit`) from the master wallet to the specified user's wallet. In the following methods, an idempotency token is used to ensure that one specific credit/debit occurs only once. The implementer is responsible for ensuring that those idempotency tokens are unique - sending the same one two times will prevent the second transaction from happening.
193
+
194
+ ```
195
+ address = OmiseGO::Balance.credit(
196
+ provider_user_id: 'some_uuid',
197
+ token_id: 'OMG:5e9c0be5-15d1-4463-9ec2-02bc8ded7120',
198
+ amount: 10_000,
199
+ idempotency_token: "123",
200
+ metadata: {}
201
+ )
202
+ ```
203
+
204
+ To use the primary balance of a specific account instead of the master account's as the sending balance, specify an `account_id`:
205
+
206
+ ```
207
+ address = OmiseGO::Balance.credit(
208
+ account_id: 'account_uuid',
209
+ provider_user_id: 'some_uuid',
210
+ token_id: 'OMG:5e9c0be5-15d1-4463-9ec2-02bc8ded7120',
211
+ amount: 10_000,
212
+ idempotency_token: "123",
213
+ metadata: {}
214
+ )
215
+ ```
216
+
217
+ #### Debit
218
+
219
+ Transfer the specified amount (as an integer, down to the `subunit_to_unit`) from the specified user's wallet back to the master wallet.
220
+
221
+ ```
222
+ address = OmiseGO::Balance.debit(
223
+ provider_user_id: 'some_uuid',
224
+ token_id: 'OMG:5e9c0be5-15d1-4463-9ec2-02bc8ded7120',
225
+ amount: 10_000,
226
+ idempotency_token: "123",
227
+ metadata: {}
228
+ )
229
+ ```
230
+
231
+ To use the primary balance of a specific account instead of the master account as the receiving balance, specify an `account_id`:
232
+
233
+ ```
234
+ address = OmiseGO::Balance.debit(
235
+ account_id: 'account_uuid',
236
+ provider_user_id: 'some_uuid',
237
+ token_id: 'OMG:5e9c0be5-15d1-4463-9ec2-02bc8ded7120',
238
+ amount: 10_000,
239
+ idempotency_token: "123",
240
+ metadata: {}
241
+ )
242
+ ```
243
+
244
+ By default, points won't be burned and will be returned to the account's primary balance (either the master's balance or the account's specified with `account_id`). If you wish to burn points, send them to a burn address. By default, a burn address identified by `'burn'` is created for each account which can be set in the `burn_balance_identifier` field:
245
+
246
+ ```
247
+ address = OmiseGO::Balance.debit(
248
+ account_id: 'account_uuid',
249
+ burn_balance_identifier: 'burn',
250
+ provider_user_id: 'some_uuid',
251
+ token_id: 'OMG:5e9c0be5-15d1-4463-9ec2-02bc8ded7120',
252
+ amount: 10_000,
253
+ idempotency_token: "123",
254
+ metadata: {}
255
+ )
256
+ ```
257
+
258
+ ### Getting settings
259
+
260
+ #### All
261
+
262
+ Retrieve the settings from the eWallet API.
263
+
264
+ ```
265
+ settings = OmiseGO::Setting.all
266
+ ```
267
+
268
+ Returns either:
269
+ - An `OmiseGO::Setting` instance
270
+ - An `OmiseGO::Error` instance
271
+
272
+
273
+ ### Listing transactions
274
+
275
+ #### Params
276
+
277
+ Some parameters can be given to the two following methods to customize the returned results. With them, the list of results can be paginated, sorted and searched.
278
+
279
+ - `page`: The page you wish to receive.
280
+ - `per_page`: The number of results per page.
281
+ - `sort_by`: The sorting field. Available values: `id`, `status`, `from`, `to`, `created_at`, `updated_at`
282
+ - `sort_dir`: The sorting direction. Available values: `asc`, `desc`
283
+ - `search_term`: A term to search for in ALL of the searchable fields. Conflict with `search_terms`, only use one of them. See list of searchable fields below (same as `search_terms`).
284
+ - `search_terms`: A hash of fields to search in:
285
+
286
+ ```
287
+ {
288
+ search_terms: {
289
+ from: "address_1"
290
+ }
291
+ }
292
+ ```
293
+
294
+ Available values: `id`, `idempotency_token`, `status`, `from`, `to`
295
+
296
+ #### All
297
+
298
+ Get the list of transactions from the eWallet API.
299
+
300
+ ```
301
+ transaction = OmiseGO::Transaction.all
302
+ ```
303
+
304
+ Returns either:
305
+ - An `OmiseGO::List` instance of `OmiseGO::Transaction` instances
306
+ - An `OmiseGO::Error` instance
307
+
308
+ Parameters can be specified in the following way:
309
+
310
+ ```
311
+ transaction = OmiseGO::Transaction.all(params: {
312
+ page: 1,
313
+ per_page: 10,
314
+ sort_by: 'created_at',
315
+ sort_dir: 'desc',
316
+ search_terms: {
317
+ from: "address_1",
318
+ to: "address_2",
319
+ status: "confirmed"
320
+ }
321
+ })
322
+ ```
323
+
324
+ #### All for user
325
+
326
+ Get the list of transactions for a specific provider user ID from the eWallet API.
327
+
328
+ ```
329
+ transaction = OmiseGO::Transaction.all_for_user(
330
+ provider_user_id: "some_uuid"
331
+ )
332
+ ```
333
+
334
+ Returns either:
335
+ - An `OmiseGO::List` instance of `OmiseGO::Transaction` instances
336
+ - An `OmiseGO::Error` instance
337
+
338
+ Parameters can be specified in the following way:
339
+
340
+ ```
341
+ transaction = OmiseGO::Transaction.all(params: {
342
+ page: 1,
343
+ per_page: 10,
344
+ sort_by: 'created_at',
345
+ sort_dir: 'desc',
346
+ search_terms: {
347
+ from: "address_1",
348
+ status: "confirmed"
349
+ }
350
+ })
351
+ ```
352
+
353
+ Since those transactions are already scoped down to the given user, it is NOT POSSIBLE to specify both `from` AND `to` in the `search_terms`. Doing so will result in the API ignoring both of those fields for the search.
354
+
355
+ ## Models
356
+
357
+ Here is the list of all the models available in the SDK with their attributes.
358
+
359
+ ### `OmiseGO::Address`
360
+
361
+ Attributes:
362
+ - `address` (string)
363
+ - `balances` (array of OmiseGO::Balance)
364
+
365
+ ### `OmiseGO::Balance`
366
+
367
+ Attributes:
368
+ - `amount` (integer)
369
+ - `minted_token` (OmiseGO::MintedToken)
370
+
371
+ ### `OmiseGO::AuthenticationToken`
372
+
373
+ Attributes:
374
+ - `authentication_token` (string)
375
+
376
+ ### `OmiseGO::Pagination`
377
+
378
+ Attributes
379
+ - `per_page` (integer)
380
+ - `current_page` (integer)
381
+ - `is_first_page` (boolean)
382
+ - `is_last_page` (boolean)
383
+
384
+ ### `OmiseGO::List`
385
+
386
+ Attributes:
387
+ - `data` (array of models)
388
+ - `pagination` (OmiseGO::Pagination)
389
+
390
+ ### `OmiseGO::MintedToken`
391
+
392
+ Attributes:
393
+ - `symbol` (string)
394
+ - `name` (string)
395
+ - `subunit_to_unit` (integer)
396
+
397
+ ### `OmiseGO::User`
398
+
399
+ Attributes:
400
+ - `id` (string)
401
+ - `username` (string)
402
+ - `provider_user_id` (string)
403
+ - `metadata` (hash)
404
+
405
+ ### `OmiseGO::Exchange`
406
+
407
+ - `rate` (integer)
408
+
409
+ ### `OmiseGO::TransactionSource`
410
+
411
+ - `address` (string)
412
+ - `amount` (integer)
413
+ - `minted_token` (`OmiseGO::MintedToken`)
414
+
415
+ ### `OmiseGO::Transaction`
416
+
417
+ - `id` (string)
418
+ - `idempotency_token` (string)
419
+ - `amount` (integer)
420
+ - `minted_token` (`OmiseGO::MintedToken`)
421
+ - `from` (`OmiseGO::TransactionSource`)
422
+ - `to` (`OmiseGO::TransactionSource`)
423
+ - `exchange` (`OmiseGO::Exchange`)
424
+ - `status` (string)
425
+ - `created_at` (string)
426
+ - `updated_at` (string)
427
+
428
+ ### `OmiseGO::Error`
429
+
430
+ Attributes:
431
+ - `code` (string)
432
+ - `description` (string)
433
+ - `messages` (hash)
434
+
435
+ ## Live Tests
436
+
437
+ Live tests are recorded using VCR. However, they have been updated to hide any access/secret key which means deleting them and re-running the live tests will fail. It is first required to update the `spec/env.rb` file with real keys. Once the VCR records have been re-generated, do not forget to replace the `Authorization` header in all of them using the base64 encoding of fake access and secret keys.
438
+
439
+ ## Development
440
+
441
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
442
+
443
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
444
+
445
+ # License
446
+
447
+ The OmiseGO Ruby SDK is released under the [Apache License](https://www.apache.org/licenses/LICENSE-2.0).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'omisego/ruby'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,11 @@
1
+ module OmiseGO
2
+ class Address < Base
3
+ attributes :address, :balances
4
+
5
+ def balances
6
+ @_balances ||= @balances.map do |balance|
7
+ Balance.new(balance)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module OmiseGO
2
+ class AuthenticationToken < Base
3
+ attributes :authentication_token
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ module OmiseGO
2
+ class Balance < Base
3
+ attributes :amount, :minted_token
4
+
5
+ class << self
6
+ def all(provider_user_id:, client: nil)
7
+ request(client).send('user.list_balances', provider_user_id: provider_user_id).data
8
+ end
9
+
10
+ def credit(provider_user_id:, token_id:, amount:, metadata: {}, idempotency_token:,
11
+ account_id: nil, burn_balance_identifier: nil, client: nil)
12
+ request(client)
13
+ .send('user.credit_balance', provider_user_id: provider_user_id,
14
+ token_id: token_id,
15
+ amount: amount,
16
+ metadata: metadata,
17
+ account_id: account_id,
18
+ burn_balance_identifier: burn_balance_identifier,
19
+ idempotency_token: idempotency_token).data
20
+ end
21
+
22
+ def debit(provider_user_id:, token_id:, amount:, metadata: {}, idempotency_token:,
23
+ account_id: nil, burn_balance_identifier: nil, client: nil)
24
+ request(client)
25
+ .send('user.debit_balance', provider_user_id: provider_user_id,
26
+ token_id: token_id,
27
+ amount: amount,
28
+ metadata: metadata,
29
+ account_id: account_id,
30
+ burn_balance_identifier: burn_balance_identifier,
31
+ idempotency_token: idempotency_token).data
32
+ end
33
+ end
34
+
35
+ def minted_token
36
+ @_minted_token ||= MintedToken.new(@minted_token)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,50 @@
1
+ module OmiseGO
2
+ class Base
3
+ class << self
4
+ attr_accessor :attributes_list
5
+
6
+ def attributes(*attrs)
7
+ attr_accessor(*attrs)
8
+ @attributes_list = attrs.map(&:to_sym)
9
+ end
10
+
11
+ def global_client
12
+ Client.new
13
+ end
14
+
15
+ def request(client)
16
+ (client || global_client).request
17
+ end
18
+ end
19
+
20
+ attr_accessor :client, :original_payload
21
+
22
+ def initialize(attributes, client: nil)
23
+ self.class.attributes_list ||= []
24
+
25
+ self.class.attributes_list.each do |name|
26
+ instance_variable_set("@#{name}", attributes[name.to_sym] ||
27
+ attributes[name.to_s])
28
+ end
29
+
30
+ self.original_payload = attributes
31
+ @client = client || self.class.global_client
32
+ end
33
+
34
+ def inspect
35
+ string = "#<#{self.class.name}:#{object_id} "
36
+ fields = self.class.attributes_list.map do |field|
37
+ "#{field}: #{send(field)}"
38
+ end
39
+ string << fields.join(', ') << '>'
40
+ end
41
+
42
+ def success?
43
+ true
44
+ end
45
+
46
+ def error?
47
+ false
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,24 @@
1
+ module OmiseGO
2
+ class Client
3
+ attr_accessor :config
4
+
5
+ def initialize(options = nil)
6
+ @config = load_config(options)
7
+ end
8
+
9
+ def call(path, params)
10
+ request.call(path: path, body: params)
11
+ end
12
+
13
+ def request
14
+ @request ||= Request.new(self)
15
+ end
16
+
17
+ private
18
+
19
+ def load_config(options)
20
+ return OmiseGO.configuration unless options
21
+ Configuration.new(options.to_hash)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,59 @@
1
+ module OmiseGO
2
+ class Configuration
3
+ OPTIONS = {
4
+ access_key: -> { ENV['OMISEGO_ACCESS_KEY'] },
5
+ secret_key: -> { ENV['OMISEGO_SECRET_KEY'] },
6
+ base_url: -> { ENV['OMISEGO_BASE_URL'] },
7
+ logger: nil
8
+ }.freeze
9
+
10
+ OMISEGO_OPTIONS = {
11
+ api_version: '1',
12
+ auth_scheme: 'OMGServer',
13
+ models: {
14
+ user: OmiseGO::User,
15
+ error: OmiseGO::Error,
16
+ authentication_token: OmiseGO::AuthenticationToken,
17
+ address: OmiseGO::Address,
18
+ balance: OmiseGO::Balance,
19
+ minted_token: OmiseGO::MintedToken,
20
+ list: OmiseGO::List,
21
+ setting: OmiseGO::Setting,
22
+ transaction: OmiseGO::Transaction,
23
+ exchange: OmiseGO::Exchange,
24
+ transaction_source: OmiseGO::TransactionSource
25
+ }
26
+ }.freeze
27
+
28
+ attr_accessor(*OPTIONS.keys)
29
+ attr_reader(*OMISEGO_OPTIONS.keys)
30
+
31
+ def initialize(options = {})
32
+ OPTIONS.each do |name, val|
33
+ value = options ? options[name] || options[name.to_sym] : nil
34
+ value ||= val.call if val.respond_to?(:call)
35
+ instance_variable_set("@#{name}", value)
36
+ end
37
+
38
+ OMISEGO_OPTIONS.each do |name, value|
39
+ instance_variable_set("@#{name}", value)
40
+ end
41
+ end
42
+
43
+ def [](option)
44
+ instance_variable_get("@#{option}")
45
+ end
46
+
47
+ def to_hash
48
+ OPTIONS.keys.each_with_object({}) do |option, hash|
49
+ hash[option.to_sym] = self[option]
50
+ end
51
+ end
52
+
53
+ def merge(options)
54
+ OPTIONS.each_key do |name|
55
+ instance_variable_set("@#{name}", options[name]) if options[name]
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,17 @@
1
+ module OmiseGO
2
+ class Error < Base
3
+ attributes :code, :description, :messages
4
+
5
+ def to_s
6
+ "#{code} - #{description}"
7
+ end
8
+
9
+ def success?
10
+ false
11
+ end
12
+
13
+ def error?
14
+ true
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module OmiseGO
2
+ class ErrorHandler
3
+ ERRORS = {
4
+ nil_id: {
5
+ code: 'user:nil_id',
6
+ description: 'The given ID was nil.'
7
+ }
8
+ }.freeze
9
+
10
+ class << self
11
+ def handle(code)
12
+ Error.new(code: ERRORS[code][:code],
13
+ description: ERRORS[code][:description])
14
+ end
15
+ end
16
+ end
17
+ end