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
+116 -30

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.
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.
@@ -18,9 +21,25 @@ If bundler is not being used to manage dependencies, install the gem by executin
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
+11 -9
View File
@@ -2,6 +2,8 @@
module StrikeMe
module API
# Factory helper object connecting the client to the models
class Account
include BaseClient
@@ -12,14 +14,15 @@ module StrikeMe
def profile
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?
body = response.body
body = JSON.parse(body) if body.is_a? String
@profile = Profile.new body.merge(account: self)
@profile = Profile.new response.body.merge(account: self)
else
# 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
@@ -36,11 +39,10 @@ module StrikeMe
return @balances if @balances.present?
response = client.get 'balances'
@balances = if response.success?
Balances.new self, response.body
if response.success?
@balances = Balances.new self, response.body
else
# if you see this, something went wrong, this is test code
ResponseError.new('balances', response)
raise ResponseError.new('balances', response.body)
end
end
+4
View File
@@ -21,6 +21,10 @@ module StrikeMe
def to_a
@items.map(&:attributes)
end
def btc
@items.find { |item| item.currency == 'BTC' }
end
end
end
end
+12 -4
View File
@@ -19,13 +19,21 @@ module StrikeMe
end
# cumbersome, but better for later debugging and handling
class ResponseError
attr_reader :url, :faraday_response
class ResponseError < StandardError
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
@faraday_response = faraday_response
@response_body = response_body
end
# def inspect
# "StrikeMe::API::BaseClient::ResponseError: #{url} status: #{faraday_response.status}"
# end
end
end
end
+6 -3
View File
@@ -3,9 +3,7 @@
module StrikeMe
module API
class Client
attr_reader :version, :connection
delegate :get, to: :connection
attr_reader :version, :connection, :last_response
def initialize(**args)
@version = args[:version] || 'v1'
@@ -23,6 +21,11 @@ module StrikeMe
builder.response :json
end
end
#delegate :get, to: :connection
def get(...)
@last_response = connection.get(...)
end
end
end
end
-1
View File
@@ -25,7 +25,6 @@ module StrikeMe
class Error < StandardError; end
MAX_NUMBER_OF_SATS = 21_000_000 * 100_000_000
NUMERICAL_OPERATORS = %i[* /].freeze
attr_reader :sats
+2 -2
View File
@@ -7,7 +7,7 @@ module StrikeMe
end
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
def serialize(value)
@@ -16,4 +16,4 @@ module StrikeMe
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(:[])
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: 200, body: '{"id":"abc44"}')
.to_return(status: 200, body: '{"id":"abc44"}', headers: {'Content-Type' => 'application/json'})
expect(subject.id).to eq 'abc44'
end
@@ -19,16 +19,66 @@ RSpec.describe StrikeMe::API::Account do
allow(ENV).to receive(:fetch)
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})
.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'
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
describe 'balances' do
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 '#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
+5 -4
View File
@@ -44,7 +44,7 @@ RSpec.describe StrikeMe::BTC do
context 'arithmetic' do
it 'supports multiplication with numbers' do
btc1 = described_class.new(234_529)
btc1 = described_class.new 234_529
btc2 = btc1 * 3
btc3 = btc2 * 2.9
@@ -61,7 +61,7 @@ RSpec.describe StrikeMe::BTC do
end
it 'supports division with numbers' do
btc1 = described_class.new(34_529)
btc1 = described_class.new 34_529
btc2 = btc1 / 3
btc3 = btc2 / 2.9
@@ -89,8 +89,8 @@ RSpec.describe StrikeMe::BTC do
end
it 'supports subtraction with other BTC objects' do
btc1 = described_class.new(34_529)
btc2 = described_class.new(2_556)
btc1 = described_class.new 34_529
btc2 = described_class.new 2_556
btc3 = btc1 - btc2
expect { btc1 - 23 }.to raise_error StrikeMe::BTC::Error
@@ -101,6 +101,7 @@ RSpec.describe StrikeMe::BTC do
end
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
expect(string).to eq '0.00000003'
expect(string.to_f.btc.sats).to eq 3