Design updates with error handling

This commit is contained in:
bterkuile committed 2025-10-18 09:13:12 -05:00
1 parent 4c73608549
commit 4b84df0af9
9 files changed
+117 -31

No files matched your search

+23 -4
View File
@@ -1,8 +1,11 @@
# StrikeMe::API StrikeMe::API
==============
This is a ruby gem to interact with the strike.me API. It is an inition proof of concept implementation. This is a ruby gem to interact with the strike.me API. It is an inition proof of concept implementation.
As a reference, the documentation of the [Strike API](https://docs.strike.me/api)
## Installation Installation
-------------
TODO: Replace `strike_me-api` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org. TODO: Replace `strike_me-api` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
@@ -18,9 +21,25 @@ If bundler is not being used to manage dependencies, install the gem by executin
gem install strike_me-api gem install strike_me-api
``` ```
## Usage Playground
-----------------
To play around with this api you can start a REPL using one of the following commands from the project's root:
```bash
pry -r strike_me/api
# or
irb -I lib -r strike_me/api
```
Then type code like:
```ruby
account = StrikeMe::API::Account.new
account.profile.attributes
account.balances.map(&:attributes)
```
TODO: Write usage instructions here Usage
--------------
TODO: Write usage instructions here, for now, look at Playground
## Development ## Development
+11 -9
View File
@@ -2,6 +2,8 @@
module StrikeMe module StrikeMe
module API module API
# Factory helper object connecting the client to the models
class Account class Account
include BaseClient include BaseClient
@@ -12,14 +14,15 @@ module StrikeMe
def profile def profile
return @profile if @profile.present? return @profile if @profile.present?
response = client.get profile_url url = profile_url
response = client.get url
#body = response.body.presence
#body = JSON.parse(body) if body.is_a? String
if response.success? if response.success?
body = response.body @profile = Profile.new response.body.merge(account: self)
body = JSON.parse(body) if body.is_a? String
@profile = Profile.new body.merge(account: self)
else else
# if you see this, something went wrong, this is test code # if you see this, something went wrong, this is test code
@profile = ResponseError.new(profile_url, response) raise ResponseError.new(url, response.body)
end end
end end
@@ -36,11 +39,10 @@ module StrikeMe
return @balances if @balances.present? return @balances if @balances.present?
response = client.get 'balances' response = client.get 'balances'
@balances = if response.success? if response.success?
Balances.new self, response.body @balances = Balances.new self, response.body
else else
# if you see this, something went wrong, this is test code raise ResponseError.new('balances', response.body)
ResponseError.new('balances', response)
end end
end end
+4
View File
@@ -21,6 +21,10 @@ module StrikeMe
def to_a def to_a
@items.map(&:attributes) @items.map(&:attributes)
end end
def btc
@items.find { |item| item.currency == 'BTC' }
end
end end
end end
end end
+13 -5
View File
@@ -19,13 +19,21 @@ module StrikeMe
end end
# cumbersome, but better for later debugging and handling # cumbersome, but better for later debugging and handling
class ResponseError class ResponseError < StandardError
attr_reader :url, :faraday_response attr_reader :url, :response_body, :status, :reason
def initialize(url, faraday_response) def initialize(url, response_body)
@reason = response_body.try(:[], 'data').try(:[], 'message')
@status = response_body.try(:[], 'data').try(:[], 'status')
super "[#{status}, {}, [\"#{reason}\"]]" if reason
@url = url @url = url
@faraday_response = faraday_response @response_body = response_body
end end
# def inspect
# "StrikeMe::API::BaseClient::ResponseError: #{url} status: #{faraday_response.status}"
# end
end end
end end
end end
+6 -3
View File
@@ -3,9 +3,7 @@
module StrikeMe module StrikeMe
module API module API
class Client class Client
attr_reader :version, :connection attr_reader :version, :connection, :last_response
delegate :get, to: :connection
def initialize(**args) def initialize(**args)
@version = args[:version] || 'v1' @version = args[:version] || 'v1'
@@ -23,6 +21,11 @@ module StrikeMe
builder.response :json builder.response :json
end end
end end
#delegate :get, to: :connection
def get(...)
@last_response = connection.get(...)
end
end end
end end
end end
-1
View File
@@ -25,7 +25,6 @@ module StrikeMe
class Error < StandardError; end class Error < StandardError; end
MAX_NUMBER_OF_SATS = 21_000_000 * 100_000_000 MAX_NUMBER_OF_SATS = 21_000_000 * 100_000_000
NUMERICAL_OPERATORS = %i[* /].freeze
attr_reader :sats attr_reader :sats
+2 -2
View File
@@ -7,7 +7,7 @@ module StrikeMe
end end
def cast(value) def cast(value)
StrikeMe::BTC.new((100_000_000 * value.to_f).to_i) StrikeMe::BTC.new((100_000_000 * value.to_f).round)
end end
def serialize(value) def serialize(value)
@@ -16,4 +16,4 @@ module StrikeMe
end end
end end
ActiveModel::Type.register(:btc, StrikeMe::BtcAttributeType) ActiveModel::Type.register(:strike_btc, StrikeMe::BtcAttributeType)
+53 -3
View File
@@ -10,7 +10,7 @@ RSpec.describe StrikeMe::API::Account do
allow(ENV).to receive(:[]) allow(ENV).to receive(:[])
expect(ENV).to receive(:[]).with('STRIKE_ACCOUNT_ID').and_return 'abc' expect(ENV).to receive(:[]).with('STRIKE_ACCOUNT_ID').and_return 'abc'
stub_request(:get, %r{https://api.strike.me/v1/accounts/abc/profile}) stub_request(:get, %r{https://api.strike.me/v1/accounts/abc/profile})
.to_return(status: 200, body: '{"id":"abc44"}') .to_return(status: 200, body: '{"id":"abc44"}', headers: {'Content-Type' => 'application/json'})
expect(subject.id).to eq 'abc44' expect(subject.id).to eq 'abc44'
end end
@@ -19,16 +19,66 @@ RSpec.describe StrikeMe::API::Account do
allow(ENV).to receive(:fetch) allow(ENV).to receive(:fetch)
expect(ENV).to receive(:fetch).with('STRIKE_ACCOUNT_HANDLE').and_return 'abcd' expect(ENV).to receive(:fetch).with('STRIKE_ACCOUNT_HANDLE').and_return 'abcd'
stub_request(:get, %r{https://api.strike.me/v1/accounts/handles/abcd/profile}) stub_request(:get, %r{https://api.strike.me/v1/accounts/handles/abcd/profile})
.to_return(status: 200, body: '{"id":"abc44"}') .to_return(status: 200, body: '{"id":"abc44"}', headers: {'Content-Type' => 'application/json'})
expect(subject.id).to eq 'abc44' expect(subject.id).to eq 'abc44'
end end
it 'throws exception when unauthorized' do
allow(ENV).to receive(:[])
expect(ENV).to receive(:[]).with('STRIKE_ACCOUNT_ID').and_return 'abc'
stub_request(:get, %r{https://api.strike.me/v1/accounts/abc/profile})
.to_return(status: 401, body: '{"data":{"status":401,"code":"UNAUTHORIZED","message":"Invalid or unspecified identity."}}', headers: {'Content-Type' => 'application/json'})
expect{ subject.id }.to raise_error '[401, {}, ["Invalid or unspecified identity."]]'
end
end end
describe 'balances' do describe 'balances' do
subject { described_class.new.balances } subject { described_class.new.balances }
before { stub_request(:get, 'https://api.strike.me/v1/balances').to_return(status: 200, body: []) } before { stub_request(:get, 'https://api.strike.me/v1/balances').to_return(status: 200, body: '[{"currency":"BTC","current":"0.00085408","pending":"0.000023","outgoing":"0.00000532","reserved":"0.00234","available":"0.00085408","total":"0.00085408"},{"currency":"USDT","current":"50.04","pending":"40.06","outgoing":"60.15","reserved":"0.00","available":"0.00","total":"0.00"}]', headers: {'Content-Type' => 'application/json'}) }
it { is_expected.to be_an_instance_of StrikeMe::API::Account::Balances } it { is_expected.to be_an_instance_of StrikeMe::API::Account::Balances }
it '#to_a properly returns the attributes' do
expect(subject.to_a).to eq [
{
"currency" => "BTC",
"current" => 0.00085408,
"pending" => 2.3e-05,
"outgoing" => 5.32e-06,
"reserved" => 0.00234,
"available" => 0.00085408,
"total" => 0.00085408
}, {
"currency" => "USDT",
"current" => 50.04,
"pending" => 40.06,
"outgoing" => 60.15,
"reserved" => 0.0,
"available" => 0.0,
"total" => 0.0
}]
end
it '#btc returns the BTC balance' do
btc = subject.btc
expect(btc).to be_present
expect(btc).to be_a StrikeMe::API::Account::Balance
expect(btc.attributes).to eq(
"available" => 0.00085408,
"currency" => "BTC",
"current" => 0.00085408,
"outgoing" => 5.32e-06,
"pending" => 2.3e-05,
"reserved" => 0.00234,
"total" => 0.00085408
)
end
it 'raises error when not authorized' do
stub_request(:get, %r{https://api.strike.me/v1/balances})
.to_return(status: 401, body: '{"data":{"status":403,"code":"FORBIDDEN","message":"Insufficient permissions."}}', headers: {'Content-Type' => 'application/json'})
expect{ subject }.to raise_error '[403, {}, ["Insufficient permissions."]]'
end
end end
end end
+5 -4
View File
@@ -44,7 +44,7 @@ RSpec.describe StrikeMe::BTC do
context 'arithmetic' do context 'arithmetic' do
it 'supports multiplication with numbers' do it 'supports multiplication with numbers' do
btc1 = described_class.new(234_529) btc1 = described_class.new 234_529
btc2 = btc1 * 3 btc2 = btc1 * 3
btc3 = btc2 * 2.9 btc3 = btc2 * 2.9
@@ -61,7 +61,7 @@ RSpec.describe StrikeMe::BTC do
end end
it 'supports division with numbers' do it 'supports division with numbers' do
btc1 = described_class.new(34_529) btc1 = described_class.new 34_529
btc2 = btc1 / 3 btc2 = btc1 / 3
btc3 = btc2 / 2.9 btc3 = btc2 / 2.9
@@ -89,8 +89,8 @@ RSpec.describe StrikeMe::BTC do
end end
it 'supports subtraction with other BTC objects' do it 'supports subtraction with other BTC objects' do
btc1 = described_class.new(34_529) btc1 = described_class.new 34_529
btc2 = described_class.new(2_556) btc2 = described_class.new 2_556
btc3 = btc1 - btc2 btc3 = btc1 - btc2
expect { btc1 - 23 }.to raise_error StrikeMe::BTC::Error expect { btc1 - 23 }.to raise_error StrikeMe::BTC::Error
@@ -101,6 +101,7 @@ RSpec.describe StrikeMe::BTC do
end end
it 'is consistent for small values' do it 'is consistent for small values' do
# if floor is used instead of round, the small number 3, multiplied by 100 million can result in 2.99999999 leading to 2
string = 3.sats.to_s string = 3.sats.to_s
expect(string).to eq '0.00000003' expect(string).to eq '0.00000003'
expect(string.to_f.btc.sats).to eq 3 expect(string.to_f.btc.sats).to eq 3