client cleanup. add user endpoint. initial specs.
This commit is contained in:
parent
938dc32747
commit
3a9d9cf6c7
32
Gemfile.lock
32
Gemfile.lock
|
@ -2,7 +2,9 @@ PATH
|
||||||
remote: .
|
remote: .
|
||||||
specs:
|
specs:
|
||||||
btcpay (0.1.0)
|
btcpay (0.1.0)
|
||||||
flexirest (~> 1.9, < 2.0)
|
activesupport (> 5)
|
||||||
|
multi_json (~> 1.15)
|
||||||
|
rest-client (~> 2.1)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
|
@ -22,16 +24,15 @@ GEM
|
||||||
safe_yaml (~> 1.0.0)
|
safe_yaml (~> 1.0.0)
|
||||||
diff-lcs (1.4.4)
|
diff-lcs (1.4.4)
|
||||||
docile (1.3.2)
|
docile (1.3.2)
|
||||||
faraday (1.0.1)
|
domain_name (0.5.20190701)
|
||||||
multipart-post (>= 1.2, < 3)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
flexirest (1.9.16)
|
factory_bot (6.1.0)
|
||||||
activesupport
|
activesupport (>= 5.0.0)
|
||||||
crack
|
|
||||||
faraday
|
|
||||||
mime-types
|
|
||||||
multi_json
|
|
||||||
hashdiff (1.0.1)
|
hashdiff (1.0.1)
|
||||||
i18n (1.8.3)
|
http-accept (1.7.0)
|
||||||
|
http-cookie (1.0.3)
|
||||||
|
domain_name (~> 0.5)
|
||||||
|
i18n (1.8.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
method_source (1.0.0)
|
method_source (1.0.0)
|
||||||
mime-types (3.3.1)
|
mime-types (3.3.1)
|
||||||
|
@ -39,7 +40,7 @@ GEM
|
||||||
mime-types-data (3.2020.0512)
|
mime-types-data (3.2020.0512)
|
||||||
minitest (5.14.1)
|
minitest (5.14.1)
|
||||||
multi_json (1.15.0)
|
multi_json (1.15.0)
|
||||||
multipart-post (2.1.1)
|
netrc (0.11.0)
|
||||||
parallel (1.19.2)
|
parallel (1.19.2)
|
||||||
parser (2.7.1.4)
|
parser (2.7.1.4)
|
||||||
ast (~> 2.4.1)
|
ast (~> 2.4.1)
|
||||||
|
@ -50,6 +51,11 @@ GEM
|
||||||
rainbow (3.0.0)
|
rainbow (3.0.0)
|
||||||
rake (12.3.3)
|
rake (12.3.3)
|
||||||
regexp_parser (1.7.1)
|
regexp_parser (1.7.1)
|
||||||
|
rest-client (2.1.0)
|
||||||
|
http-accept (>= 1.7.0, < 2.0)
|
||||||
|
http-cookie (>= 1.0.2, < 2.0)
|
||||||
|
mime-types (>= 1.16, < 4.0)
|
||||||
|
netrc (~> 0.8)
|
||||||
rexml (3.2.4)
|
rexml (3.2.4)
|
||||||
rspec (3.9.0)
|
rspec (3.9.0)
|
||||||
rspec-core (~> 3.9.0)
|
rspec-core (~> 3.9.0)
|
||||||
|
@ -84,6 +90,9 @@ GEM
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
tzinfo (1.2.7)
|
tzinfo (1.2.7)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
|
unf (0.1.4)
|
||||||
|
unf_ext
|
||||||
|
unf_ext (0.0.7.7)
|
||||||
unicode-display_width (1.7.0)
|
unicode-display_width (1.7.0)
|
||||||
vcr (6.0.0)
|
vcr (6.0.0)
|
||||||
webmock (3.8.3)
|
webmock (3.8.3)
|
||||||
|
@ -98,6 +107,7 @@ PLATFORMS
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
btcpay!
|
btcpay!
|
||||||
bundler (~> 2.0)
|
bundler (~> 2.0)
|
||||||
|
factory_bot (~> 6.1)
|
||||||
pry (> 0)
|
pry (> 0)
|
||||||
rake (~> 12.0)
|
rake (~> 12.0)
|
||||||
rspec (~> 3.0)
|
rspec (~> 3.0)
|
||||||
|
|
55
README.md
55
README.md
|
@ -22,7 +22,58 @@ Or install it yourself as:
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
TODO: Write usage instructions here
|
#### Required Params
|
||||||
|
|
||||||
|
##### Auth Token
|
||||||
|
|
||||||
|
At least one of the following auth tokens are required. Auth tokens can be created via the following:
|
||||||
|
|
||||||
|
1. `auth_token`
|
||||||
|
- Scoped Api Tokens can be created via `/Manage/APIKeys`
|
||||||
|
- `BTCPAY_AUTH_TOKEN` environment variable can also be used
|
||||||
|
1. `basic_auth_token`
|
||||||
|
- Legacy Api Key can be created per store via `/stores/{store-id}/Tokens`
|
||||||
|
- `BTCPAY_BASIC_AUTH_TOKEN` environment variable can also be used
|
||||||
|
|
||||||
|
##### Base Url
|
||||||
|
|
||||||
|
A `base_url` is required to interact with the server.
|
||||||
|
- `BTCPAY_BASE_URL` environment variable can also be used
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
client = BtcPay.new(auth_token: 'foobar', base_url: 'http://localhost:49392')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response
|
||||||
|
|
||||||
|
A response consists of the following accessible attributes:
|
||||||
|
|
||||||
|
1. `#body` - rubified response body
|
||||||
|
1. `#code` - response status code
|
||||||
|
1. `#headers` - response headers
|
||||||
|
1. `#raw` - unaltered response body
|
||||||
|
1. `#status` - `:success`/`:failure`
|
||||||
|
|
||||||
|
### Request object types
|
||||||
|
|
||||||
|
All endpoints are accessed via namespaced Api resource. Example: `client.users.create({ email: 'foo@bar.com', password: 'password', isAdministrator: false })`
|
||||||
|
|
||||||
|
#### Users:
|
||||||
|
|
||||||
|
1. `#me(**opts)`
|
||||||
|
- alias: `get`, `show`
|
||||||
|
1. `#create(payload, **opts)`
|
||||||
|
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
`btcpay` can be initialized with either arguments or ENV:
|
||||||
|
|
||||||
|
| Variable | Description | Default |
|
||||||
|
| --------------------------|:------------------------|:--------:|
|
||||||
|
| `BTCPAY_AUTH_TOKEN` | BtcPay Auth Token | - |
|
||||||
|
| `BTCPAY_BASIC_AUTH_TOKEN` | BtcPay Basic Auth Token | - |
|
||||||
|
| `BTCPAY_BASE_URL` | BtcPay Base Url | - |
|
||||||
|
|
||||||
### BtcPay Docker Compose
|
### BtcPay Docker Compose
|
||||||
|
|
||||||
|
@ -42,7 +93,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/btcpay.
|
Bug reports and pull requests are welcome on Gitlab at https://gitlab.com/snogrammer/btcpay.
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
|
@ -28,9 +28,12 @@ Gem::Specification.new do |spec|
|
||||||
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
||||||
spec.require_paths = ['lib']
|
spec.require_paths = ['lib']
|
||||||
|
|
||||||
spec.add_dependency 'flexirest', '~> 1.9', '< 2.0'
|
spec.add_dependency 'activesupport', '> 5'
|
||||||
|
spec.add_dependency 'multi_json', '~> 1.15'
|
||||||
|
spec.add_dependency 'rest-client', '~> 2.1'
|
||||||
|
|
||||||
spec.add_development_dependency 'bundler', '~> 2.0'
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
||||||
|
spec.add_development_dependency 'factory_bot', '~> 6.1'
|
||||||
spec.add_development_dependency 'pry', '> 0'
|
spec.add_development_dependency 'pry', '> 0'
|
||||||
spec.add_development_dependency 'rubocop', '> 0'
|
spec.add_development_dependency 'rubocop', '> 0'
|
||||||
spec.add_development_dependency 'simplecov', '> 0'
|
spec.add_development_dependency 'simplecov', '> 0'
|
||||||
|
|
|
@ -2,7 +2,16 @@
|
||||||
|
|
||||||
require 'btcpay/version'
|
require 'btcpay/version'
|
||||||
|
|
||||||
|
require 'btcpay/client/config'
|
||||||
|
require 'btcpay/client/base'
|
||||||
|
|
||||||
module BtcPay
|
module BtcPay
|
||||||
class Error < StandardError; end
|
class Error < StandardError; end
|
||||||
# Your code goes here...
|
|
||||||
|
module_function
|
||||||
|
|
||||||
|
def new(**args)
|
||||||
|
config = BtcPay::Client::Config.new(**args)
|
||||||
|
BtcPay::Client::Base.new(config: config, **args)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module BtcPay
|
||||||
|
module Client
|
||||||
|
module Api
|
||||||
|
class Base
|
||||||
|
def initialize(client:, logger:)
|
||||||
|
@client = client
|
||||||
|
@logger = logger
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def base_path
|
||||||
|
raise NotImplementedError.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def path(*args)
|
||||||
|
request_path = args.prepend(base_path.presence).compact.join('/')
|
||||||
|
request_path[0].eql?('/') ? request_path : '/' + request_path
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :client, :logger
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require_relative './users'
|
|
@ -0,0 +1,30 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module BtcPay
|
||||||
|
module Client
|
||||||
|
module Api
|
||||||
|
class Users < Base
|
||||||
|
PATH = '/users'
|
||||||
|
|
||||||
|
# @see https://docs.btcpayserver.org/API/Greenfield/v1/#operation/Users_GetCurrentUser
|
||||||
|
def me(**opts)
|
||||||
|
client.get(path('me'), options: opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
alias get me
|
||||||
|
alias show me
|
||||||
|
|
||||||
|
# @see https://docs.btcpayserver.org/API/Greenfield/v1/#tag/Users/paths/~1api~1v1~1users/post
|
||||||
|
def create(payload, **opts)
|
||||||
|
client.post(path, payload: payload, options: opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def base_path
|
||||||
|
PATH
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,129 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'active_support'
|
||||||
|
require 'active_support/core_ext/object'
|
||||||
|
require 'rest_client'
|
||||||
|
|
||||||
|
require_relative './result'
|
||||||
|
require_relative './api/base'
|
||||||
|
|
||||||
|
module BtcPay
|
||||||
|
module Client
|
||||||
|
class Error < RuntimeError
|
||||||
|
def initialize(message)
|
||||||
|
super(message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Base
|
||||||
|
API_PATH = '/api/v1'
|
||||||
|
DEFAULT_TIMEOUT = 10
|
||||||
|
|
||||||
|
# @param config [BtcPay::Client::Config]
|
||||||
|
# @param logger [Logger]
|
||||||
|
# @param timeout [Integer] Defaults to DEFAULT_TIMEOUT
|
||||||
|
def initialize(config:, logger: Logger.new(STDOUT), timeout: DEFAULT_TIMEOUT, **_kwargs)
|
||||||
|
@config = config
|
||||||
|
@logger = logger
|
||||||
|
@timeout = timeout
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET request
|
||||||
|
#
|
||||||
|
# @param uri [String]
|
||||||
|
# @param options [Hash]
|
||||||
|
# @param headers [Hash]
|
||||||
|
# @return [Result]
|
||||||
|
def get(uri, options: {}, headers: {})
|
||||||
|
request(uri, method: :get, options: options, headers: headers)
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST request
|
||||||
|
#
|
||||||
|
# @param uri [String]
|
||||||
|
# @param payload [Hash]
|
||||||
|
# @param options [Hash]
|
||||||
|
# @param headers [Hash]
|
||||||
|
# @return [Result]
|
||||||
|
def post(uri, payload:, options: {}, headers: {})
|
||||||
|
data = payload.is_a?(Hash) ? payload.to_json : payload
|
||||||
|
request(uri, method: :post, payload: data, options: options, headers: headers)
|
||||||
|
end
|
||||||
|
|
||||||
|
def users
|
||||||
|
@users ||= Api::Users.new(client: self, logger: logger)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :config, :logger
|
||||||
|
|
||||||
|
# @return [Hash]
|
||||||
|
def default_headers
|
||||||
|
{
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
'User-Agent' => config.user_agent,
|
||||||
|
'Accept-Encoding' => 'deflate, gzip',
|
||||||
|
'Authorization' => config.authorization
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
# @yield [Result]
|
||||||
|
def request(uri, method:, payload: nil, options: {}, headers: {})
|
||||||
|
options ||= {}
|
||||||
|
headers ||= {}
|
||||||
|
|
||||||
|
url = URI(config.base_url)
|
||||||
|
url.path = API_PATH + uri
|
||||||
|
url.query = CGI.unescape(options.to_query).presence
|
||||||
|
|
||||||
|
params = {
|
||||||
|
method: method,
|
||||||
|
url: url.to_s,
|
||||||
|
payload: payload,
|
||||||
|
headers: default_headers.merge(headers),
|
||||||
|
open_timeout: open_timeout,
|
||||||
|
timeout: timeout
|
||||||
|
}.compact
|
||||||
|
|
||||||
|
response = RestClient::Request.execute(params)
|
||||||
|
|
||||||
|
logger.debug(message: 'GET Request', url: url, options: options, status: response.code)
|
||||||
|
result = success?(response.code) ? Result.success(response) : Result.failed(response)
|
||||||
|
|
||||||
|
logger.warn(error: 'Request Error', code: result.code, body: result.raw) if result.failure?
|
||||||
|
result
|
||||||
|
rescue ::RestClient::GatewayTimeout
|
||||||
|
raise Error.new('Gateway timeout')
|
||||||
|
rescue ::RestClient::RequestTimeout
|
||||||
|
raise Error.new('Request timeout')
|
||||||
|
rescue ::RestClient::Exception => e
|
||||||
|
handle_error(e)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Handle errors
|
||||||
|
# @param error [Error]
|
||||||
|
# @return [Result]
|
||||||
|
def handle_error(error)
|
||||||
|
logger.error(error: 'Request Exception', code: error.response.code, message: error.message)
|
||||||
|
|
||||||
|
Result.failed(response)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Integer]
|
||||||
|
def open_timeout
|
||||||
|
@open_timeout || DEFAULT_TIMEOUT
|
||||||
|
end
|
||||||
|
|
||||||
|
# @return [Integer]
|
||||||
|
def timeout
|
||||||
|
@timeout || DEFAULT_TIMEOUT
|
||||||
|
end
|
||||||
|
|
||||||
|
def success?(response_code)
|
||||||
|
response_code.in?(200..299)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,54 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module BtcPay
|
||||||
|
module Client
|
||||||
|
class Config
|
||||||
|
AUTH_TOKEN_TYPE = 'token'
|
||||||
|
BASIC_TOKEN_TYPE = 'Basic'
|
||||||
|
|
||||||
|
attr_reader :authorization, :auth_token, :basic_auth_token, :base_url, :user_agent
|
||||||
|
|
||||||
|
def initialize(**kwargs)
|
||||||
|
@base_url = load_url(kwargs[:base_url])
|
||||||
|
@user_agent = kwargs[:user_agent] || "btcpay_ruby/#{BtcPay::VERSION}"
|
||||||
|
|
||||||
|
load_auth_token(kwargs)
|
||||||
|
set_authorization
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_h
|
||||||
|
{
|
||||||
|
auth_token: auth_token,
|
||||||
|
basic_auth_token: basic_auth_token,
|
||||||
|
base_url: base_url,
|
||||||
|
user_agent: user_agent
|
||||||
|
}.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_url(url)
|
||||||
|
base_url = url || ENV['BTCPAY_BASE_URL'].to_s
|
||||||
|
uri = URI(base_url)
|
||||||
|
return uri.to_s if uri.scheme && uri.host
|
||||||
|
|
||||||
|
raise ArgumentError.new('invalid base_url')
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_auth_token(kwargs)
|
||||||
|
@auth_token = kwargs[:auth_token] || ENV['BTCPAY_AUTH_TOKEN']
|
||||||
|
@basic_auth_token = kwargs[:basic_auth_token] || ENV['BTCPAY_BASIC_AUTH_TOKEN']
|
||||||
|
|
||||||
|
raise ArgumentError.new('auth_token or basic_auth_token required') unless @auth_token || @basic_auth_token
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_authorization
|
||||||
|
@authorization = if @auth_token
|
||||||
|
"#{AUTH_TOKEN_TYPE} #{@auth_token}"
|
||||||
|
elsif @basic_auth_token
|
||||||
|
"#{BASIC_TOKEN_TYPE} #{@basic_auth_token}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,76 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'active_support/core_ext/hash/indifferent_access'
|
||||||
|
require 'multi_json'
|
||||||
|
|
||||||
|
module BtcPay
|
||||||
|
module Client
|
||||||
|
##
|
||||||
|
# Status object to capture result from an HTTP request
|
||||||
|
#
|
||||||
|
# Gives callers context of the result and allows them to
|
||||||
|
# implement successful strategies to handle success/failure
|
||||||
|
class Result
|
||||||
|
def self.success(response)
|
||||||
|
new(:success, response)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.failed(response)
|
||||||
|
new(:failed, response)
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :body, :code, :headers, :raw, :status
|
||||||
|
|
||||||
|
def initialize(status, response)
|
||||||
|
@raw = raw_parse(response.body)
|
||||||
|
|
||||||
|
@body = rubify_body
|
||||||
|
@code = response.code
|
||||||
|
@headers = response.headers # e.g. "Content-Type" will become :content_type.
|
||||||
|
@status = status
|
||||||
|
end
|
||||||
|
|
||||||
|
def success?
|
||||||
|
status == :success
|
||||||
|
end
|
||||||
|
|
||||||
|
def failure?
|
||||||
|
!success?
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_h
|
||||||
|
{
|
||||||
|
status: status,
|
||||||
|
headers: headers,
|
||||||
|
code: code,
|
||||||
|
body: body
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
alias to_hash to_h
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def method_missing(method, *args, &blk)
|
||||||
|
to_h.send(method, *args, &blk) || super
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_to_missing?(method, include_private = false)
|
||||||
|
to_h.respond_to?(method) || super
|
||||||
|
end
|
||||||
|
|
||||||
|
# @param body [JSON] Raw JSON body
|
||||||
|
def raw_parse(body)
|
||||||
|
MultiJson.load(body).with_indifferent_access
|
||||||
|
rescue StandardError => e
|
||||||
|
raise ResponseBodyParseError.new(error: 'JSON parse error', message: e.message, body: body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def rubify_body
|
||||||
|
raw.deep_transform_keys { |key| key.to_s.underscore }.with_indifferent_access
|
||||||
|
end
|
||||||
|
|
||||||
|
class ResponseBodyParseError < StandardError; end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,4 +4,11 @@ RSpec.describe BtcPay do
|
||||||
it 'has a version number' do
|
it 'has a version number' do
|
||||||
expect(BtcPay::VERSION).to eq('0.1.0')
|
expect(BtcPay::VERSION).to eq('0.1.0')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.new' do
|
||||||
|
it do
|
||||||
|
expect(described_class.new(auth_token: '123', base_url: 'http://localhost'))
|
||||||
|
.to be_a(BtcPay::Client::Base)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe BtcPay::Client::Base do
|
||||||
|
let(:config) { create(:config) }
|
||||||
|
let(:subject) { described_class.new(config: config) }
|
||||||
|
|
||||||
|
describe '.configuration' do
|
||||||
|
it 'returns host' do
|
||||||
|
expect(subject.host).to eq(host)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns api_key' do
|
||||||
|
expect(subject.api_key).to eq(api_key)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns sandbox' do
|
||||||
|
expect(subject.sandbox).to eq(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
# skipped due to logger being mocked
|
||||||
|
xit 'returns default logger' do
|
||||||
|
expect(subject.logger).to be_a(Logger)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises error when api_key missing' do
|
||||||
|
expect { described_class.new }.to raise_error(KeyError, 'key not found: "CMC_PRO_API_KEY"')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'host' do
|
||||||
|
it 'defaults to production environment' do
|
||||||
|
subject = described_class.new(api_key: api_key)
|
||||||
|
expect(subject.sandbox).to eq(false)
|
||||||
|
expect(subject.host).to eq('https://pro-api.coinmarketcap.com/v1')
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'defaults to production environment' do
|
||||||
|
subject = described_class.new(api_key: api_key, sandbox: true)
|
||||||
|
expect(subject.sandbox).to eq(true)
|
||||||
|
expect(subject.host).to eq('https://sandbox.coinmarketcap.com/v1')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'services' do
|
||||||
|
it '.cryptocurrency' do
|
||||||
|
expect(subject.cryptocurrency).to be_a(CoinMarketPro::Endpoint::Cryptocurrency)
|
||||||
|
end
|
||||||
|
|
||||||
|
it '.exchange' do
|
||||||
|
expect(subject.exchange).to be_a(CoinMarketPro::Endpoint::Exchange)
|
||||||
|
end
|
||||||
|
|
||||||
|
it '.global_metrics' do
|
||||||
|
expect(subject.global_metrics).to be_a(CoinMarketPro::Endpoint::GlobalMetrics)
|
||||||
|
end
|
||||||
|
|
||||||
|
it '.tools' do
|
||||||
|
expect(subject.tools).to be_a(CoinMarketPro::Endpoint::Tools)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.default_headers' do
|
||||||
|
it do
|
||||||
|
expect(subject.default_headers).to eq(
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
'User-Agent' => "coin_market_pro/#{CoinMarketPro::VERSION}",
|
||||||
|
'Accept-Encoding' => 'deflate, gzip',
|
||||||
|
'X-CMC_PRO_API_KEY' => api_key
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.open_timeout' do
|
||||||
|
it { expect(subject.open_timeout).to eq(10) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.timeout' do
|
||||||
|
it { expect(subject.timeout).to eq(10) }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.handle_error' do
|
||||||
|
let(:response) { double('response') }
|
||||||
|
let(:error) { double('error') }
|
||||||
|
let(:code) { 400 }
|
||||||
|
let(:body) do
|
||||||
|
{
|
||||||
|
data: { id: 1 },
|
||||||
|
status: { code: code }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns failure status response' do
|
||||||
|
allow(response).to receive(:headers)
|
||||||
|
allow(response).to receive(:body).and_return(body.to_json)
|
||||||
|
expect(response).to receive(:code).at_least(:once).and_return(code)
|
||||||
|
|
||||||
|
expect(error).to receive(:message).and_return('message')
|
||||||
|
expect(error).to receive(:response).and_return(response)
|
||||||
|
|
||||||
|
status = subject.handle_error(error)
|
||||||
|
|
||||||
|
expect(status.failure?).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.get' do
|
||||||
|
let(:response) { double('response') }
|
||||||
|
let(:body) do
|
||||||
|
{
|
||||||
|
data: { id: 1 },
|
||||||
|
status: { code: 200 }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(RestClient::Request).to receive(:execute).and_return(response)
|
||||||
|
allow(response).to receive(:body).and_return(body.to_json)
|
||||||
|
allow(response).to receive(:headers)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns successful status response' do
|
||||||
|
expect(response).to receive(:code).at_least(:once).and_return(200)
|
||||||
|
|
||||||
|
status = subject.get(path)
|
||||||
|
expect(status.success?).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'error' do
|
||||||
|
it 'returns failure status response' do
|
||||||
|
expect(response).to receive(:code).at_least(:once).and_return(400)
|
||||||
|
|
||||||
|
status = subject.get(path)
|
||||||
|
expect(status.failure?).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,56 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe BtcPay::Client::Config do
|
||||||
|
let(:config) { build(:config) }
|
||||||
|
|
||||||
|
describe '#initialize' do
|
||||||
|
context '#base_url' do
|
||||||
|
it do
|
||||||
|
subject = described_class.new(**config.to_h)
|
||||||
|
expect(subject.base_url).to eq(config.base_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
it { expect { described_class.new(auth_token: 123) }.to raise_error(ArgumentError, 'invalid base_url') }
|
||||||
|
end
|
||||||
|
|
||||||
|
context '#authorization' do
|
||||||
|
it do
|
||||||
|
expect { described_class.new(base_url: config.base_url) }
|
||||||
|
.to raise_error(ArgumentError, 'auth_token or basic_auth_token required')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'auth_token' do
|
||||||
|
it do
|
||||||
|
subject = described_class.new(**config.to_h)
|
||||||
|
expect(subject.auth_token).to eq(config.auth_token)
|
||||||
|
expect(subject.authorization).to eq("token #{config.auth_token}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'basic_auth_token' do
|
||||||
|
let(:config) { build(:config, :basic_auth_token) }
|
||||||
|
|
||||||
|
it do
|
||||||
|
subject = described_class.new(**config.to_h)
|
||||||
|
expect(subject.basic_auth_token).to eq(config.basic_auth_token)
|
||||||
|
expect(subject.authorization).to eq("Basic #{config.basic_auth_token}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_h' do
|
||||||
|
subject { described_class.new(**config.to_h) }
|
||||||
|
|
||||||
|
it 'returns hash' do
|
||||||
|
expect(subject.to_h).to eq({
|
||||||
|
auth_token: config.auth_token,
|
||||||
|
basic_auth_token: config.basic_auth_token,
|
||||||
|
base_url: config.base_url,
|
||||||
|
user_agent: config.user_agent
|
||||||
|
}.compact)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,90 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe CoinMarketPro::Client::Result do # rubocop:disable Metrics/BlockLength
|
||||||
|
let(:response) { double('response') }
|
||||||
|
let(:code) { double('code') }
|
||||||
|
let(:body) do
|
||||||
|
{
|
||||||
|
data: {
|
||||||
|
test: 'test'
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
code: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
let(:headers) do
|
||||||
|
{
|
||||||
|
content_type: 'application/json'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
allow(response).to receive(:body).and_return(body.to_json)
|
||||||
|
allow(response).to receive(:code).and_return(code)
|
||||||
|
allow(response).to receive(:headers).and_return(headers)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.success' do
|
||||||
|
it 'captures response and sets status to success' do
|
||||||
|
result = described_class.success(response)
|
||||||
|
|
||||||
|
expect(result).to be_a(CoinMarketPro::Client::Result)
|
||||||
|
expect(result.success?).to eq(true)
|
||||||
|
expect(result.failure?).to eq(false)
|
||||||
|
expect(result.body).to eq(body[:data])
|
||||||
|
expect(result.code).to eq(code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.failed' do
|
||||||
|
it 'captures response and sets status to failed' do
|
||||||
|
result = described_class.failed(response)
|
||||||
|
|
||||||
|
expect(result).to be_a(CoinMarketPro::Client::Result)
|
||||||
|
expect(result.success?).to eq(false)
|
||||||
|
expect(result.failure?).to eq(true)
|
||||||
|
expect(result.body).to eq(body[:data])
|
||||||
|
expect(result.code).to eq(code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#success?' do
|
||||||
|
let(:result) { described_class.success(response) }
|
||||||
|
|
||||||
|
it 'returns true if success' do
|
||||||
|
expect(result.success?).to eq(true)
|
||||||
|
expect(result.failure?).to eq(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#failure?' do
|
||||||
|
let(:result) { described_class.failed(response) }
|
||||||
|
|
||||||
|
it 'returns true if failed' do
|
||||||
|
expect(result.failure?).to eq(true)
|
||||||
|
expect(result.success?).to eq(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_h' do
|
||||||
|
it 'returns a hash of values' do
|
||||||
|
result = described_class.success(response)
|
||||||
|
expect(result.to_h).to eq(code: code, headers: headers, body: body[:data], status: body[:status].merge(result: :success))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_hash' do
|
||||||
|
it 'returns a hash of values' do
|
||||||
|
result = described_class.success(response)
|
||||||
|
expect(result.to_hash).to eq(code: code, headers: headers, body: body[:data], status: body[:status].merge(result: :success))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can be called implicitly' do
|
||||||
|
result = described_class.success(response)
|
||||||
|
expect(result[:code]).to eq code
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :users_payload, class: Hash do
|
||||||
|
email { 'foo@bar.com' }
|
||||||
|
password { 'password' }
|
||||||
|
isAdministrator { false }
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :config, class: BtcPay::Client::Config do
|
||||||
|
base_url { 'http://localhost:49392' }
|
||||||
|
|
||||||
|
auth_token # default trait
|
||||||
|
|
||||||
|
initialize_with { new(**attributes) }
|
||||||
|
|
||||||
|
trait :auth_token do
|
||||||
|
auth_token { '9133b8ef3ae9a4b7f2d9a6efef1d5cf738067c68' }
|
||||||
|
basic_auth_token { nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
trait :basic_auth_token do
|
||||||
|
auth_token { nil }
|
||||||
|
basic_auth_token { 'TODO' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,6 +7,9 @@ require 'bundler/setup'
|
||||||
require 'btcpay'
|
require 'btcpay'
|
||||||
|
|
||||||
require 'webmock/rspec'
|
require 'webmock/rspec'
|
||||||
|
require 'pry'
|
||||||
|
|
||||||
|
Dir[File.join(__dir__, 'support', '**', '*.rb')].sort.each { |f| require f }
|
||||||
|
|
||||||
RSpec.configure do |config|
|
RSpec.configure do |config|
|
||||||
# Enable flags like --only-failures and --next-failure
|
# Enable flags like --only-failures and --next-failure
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'factory_bot'
|
||||||
|
|
||||||
|
RSpec.configure do |config|
|
||||||
|
config.include FactoryBot::Syntax::Methods
|
||||||
|
|
||||||
|
config.before(:suite) do
|
||||||
|
FactoryBot.find_definitions
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue