Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,28 @@ puts response.to_hash()

n.b. the keys of the response hash will always be symbols.

## Configuration

You may optionally supply a config argument with your API key:

```ruby
require 'button'

client = Button::Client.new('sk-XXX', {
hostname: 'api.testsite.com',
port: 3000,
secure: false,
timeout: 5 # seconds
})
```

The supported options are as follows:

* `hostname`: Defaults to `api.usebutton.com`.
* `port`: Defaults to `443` if `config.secure`, else defaults to `80`.
* `secure`: Whether or not to use HTTPS. Defaults to True. **N.B: Button's API is only exposed through HTTPS. This option is provided purely as a convenience for testing and development.**
* `timeout`: The time in seconds that may elapse before network requests abort. Defaults to `nil`.

## Resources

We currently expose only one resource to manage, `Orders`.
Expand Down
18 changes: 16 additions & 2 deletions lib/button/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,28 @@ module Button
# puts client.orders.get("btnorder-XXX")
#
class Client
def initialize(api_key)
def initialize(api_key, config = {})
if api_key.nil? || api_key.empty?
raise ButtonClientError, NO_API_KEY_MESSAGE
end

@orders = Orders.new(api_key)
config_with_defaults = merge_defaults(config)

@orders = Orders.new(api_key, config_with_defaults)
end

def merge_defaults(config)
secure = config.fetch(:secure, true)

return {
secure: secure,
timeout: config.fetch(:timeout, nil),
hostname: config.fetch(:hostname, 'api.usebutton.com'),
port: config.fetch(:port, secure ? 443 : 80)
}
end

attr_reader :orders
private :merge_defaults
end
end
18 changes: 13 additions & 5 deletions lib/button/resources/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,21 @@ module Button
# end
#
class Resource
HOST = 'api.usebutton.com'.freeze
PORT = 443
USER_AGENT = "Button/#{Button::VERSION} ruby/#{RUBY_VERSION}".freeze

def initialize(api_key)
def initialize(api_key, config)
@api_key = api_key
@http = Net::HTTP.new(HOST, PORT)
@http.use_ssl = true
@config = config
@http = Net::HTTP.new(config[:hostname], config[:port])
@http.use_ssl = config[:secure]

if not config[:timeout].nil?
@http.read_timeout = config[:timeout]
end
end

def timeout
@http.read_timeout
end

# Performs an HTTP GET at the provided path.
Expand Down Expand Up @@ -96,6 +103,7 @@ def process_response(response)
raise ButtonClientError, "Invalid response: #{parsed}"
end

attr_accessor :config
private :api_request, :process_response
end
end
2 changes: 1 addition & 1 deletion lib/button/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Button
VERSION = '1.0.0'.freeze
VERSION = '1.1.0'.freeze
end
27 changes: 27 additions & 0 deletions test/button/client_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,31 @@ def test_exposes_orders
client = Button::Client.new('sk-XXX')
assert_respond_to(client, :orders)
end

def test_sets_default_config_parameters
client = Button::Client.new('sk-XXX')
assert_equal(client.orders.config, {
hostname: 'api.usebutton.com',
port: 443,
secure: true,
timeout: nil
})
end

def test_allows_config_overrides
config = {
hostname: 'localhost',
port: 8080,
secure: false,
timeout: 20
}

client = Button::Client.new('sk-XXX', config)
assert_equal(client.orders.config, config)
end

def test_sets_default_port_properly
client = Button::Client.new('sk-XXX', secure: false)
assert_equal(client.orders.config[:port], 80)
end
end
17 changes: 13 additions & 4 deletions test/button/resources/orders_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
require File.expand_path('../../../test_helper', __FILE__)

class OrdersTest < Test::Unit::TestCase
def setup
@orders = Button::Orders.new('sk-XXX', {
secure: true,
timeout: nil,
hostname: 'api.usebutton.com',
port: 443
})
end

def teardown
WebMock.reset!
end
Expand All @@ -10,7 +19,7 @@ def test_get
.with(headers: { Authorization: 'Basic c2stWFhYOg==' })
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')

response = Button::Orders.new('sk-XXX').get('btnorder-XXX')
response = @orders.get('btnorder-XXX')
assert_equal(response.a, 1)
end

Expand All @@ -19,7 +28,7 @@ def test_create
.with(body: '{"a":1}', headers: { Authorization: 'Basic c2stWFhYOg==' })
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')

response = Button::Orders.new('sk-XXX').create(a: 1)
response = @orders.create(a: 1)
assert_equal(response.a, 1)
end

Expand All @@ -28,7 +37,7 @@ def test_update
.with(body: '{"a":1}', headers: { Authorization: 'Basic c2stWFhYOg==' })
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')

response = Button::Orders.new('sk-XXX').update('btnorder-XXX', a: 1)
response = @orders.update('btnorder-XXX', a: 1)
assert_equal(response.a, 1)
end

Expand All @@ -37,7 +46,7 @@ def test_delete
.with(headers: { Authorization: 'Basic c2stWFhYOg==' })
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": null }')

response = Button::Orders.new('sk-XXX').delete('btnorder-XXX')
response = @orders.delete('btnorder-XXX')
assert_equal(response.to_hash, {})
end
end
47 changes: 36 additions & 11 deletions test/button/resources/resource_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
require File.expand_path('../../../test_helper', __FILE__)

class ResourceTest < Test::Unit::TestCase
def setup
@resource = Button::Resource.new('sk-XXX', {
secure: true,
timeout: nil,
hostname: 'api.usebutton.com',
port: 443
})
end

def teardown
WebMock.reset!
end
Expand All @@ -10,7 +19,7 @@ def test_raises_with_empty_response
.to_return(status: 200, body: '')

assert_raises(Button::ButtonClientError) do
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
@resource.api_get('/v1/bloop')
end
end

Expand All @@ -19,7 +28,7 @@ def test_raises_with_nil_response
.to_return(status: 200, body: nil)

assert_raises(Button::ButtonClientError) do
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
@resource.api_get('/v1/bloop')
end
end

Expand All @@ -28,7 +37,7 @@ def test_raises_with_invalid_json_response
.to_return(status: 200, body: 'invalid json')

assert_raises(Button::ButtonClientError) do
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
@resource.api_get('/v1/bloop')
end
end

Expand All @@ -37,7 +46,7 @@ def test_raises_with_a_server_error
.to_return(status: 404, body: '{ "meta": { "status": "error" }, "error": { "message": "bloop" } }')

assert_raises(Button::ButtonClientError.new('bloop')) do
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
@resource.api_get('/v1/bloop')
end
end

Expand All @@ -46,7 +55,7 @@ def test_raises_with_an_unknown_status_error
.to_return(status: 404, body: '{ "meta": { "status": "wat" }, "error": { "message": "bloop" } }')

assert_raises(Button::ButtonClientError.new('Unknown status: wat')) do
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
@resource.api_get('/v1/bloop')
end
end

Expand All @@ -55,7 +64,7 @@ def test_raises_if_receives_unknown_error_response
.to_return(status: 404, body: '{}')

assert_raises(Button::ButtonClientError) do
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
@resource.api_get('/v1/bloop')
end

WebMock.reset!
Expand All @@ -64,7 +73,7 @@ def test_raises_if_receives_unknown_error_response
.to_return(status: 404, body: '{ "meta": "wat" }')

assert_raises(Button::ButtonClientError) do
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
@resource.api_get('/v1/bloop')
end

WebMock.reset!
Expand All @@ -73,7 +82,7 @@ def test_raises_if_receives_unknown_error_response
.to_return(status: 404, body: '{ "meta": { "status": "error" }, "error": "wat" }')

assert_raises(Button::ButtonClientError) do
Button::Resource.new('sk-XXX').api_get('/v1/bloop')
@resource.api_get('/v1/bloop')
end

WebMock.reset!
Expand All @@ -83,7 +92,7 @@ def test_gets_a_resource
stub_request(:get, 'https://api.usebutton.com/v1/bloop')
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')

response = Button::Resource.new('sk-XXX').api_get('/v1/bloop')
response = @resource.api_get('/v1/bloop')
assert_equal(response.a, 1)
end

Expand All @@ -92,15 +101,31 @@ def test_posts_a_resource
.with(body: '{"a":1}')
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')

response = Button::Resource.new('sk-XXX').api_post('/v1/bloop', a: 1)
response = @resource.api_post('/v1/bloop', a: 1)
assert_equal(response.a, 1)
end

def test_deletes_a_resource
stub_request(:delete, 'https://api.usebutton.com/v1/bloop/1')
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": null }')

response = Button::Resource.new('sk-XXX').api_delete('/v1/bloop/1')
response = @resource.api_delete('/v1/bloop/1')
assert_equal(response.to_hash, {})
end

def test_uses_config
stub_request(:get, 'http://localhost:8080/v1/bloop')
.to_return(status: 200, body: '{ "meta": { "status": "ok" }, "object": { "a": 1 } }')

resource = Button::Resource.new('sk-XXX', {
secure: false,
timeout: 1989,
hostname: 'localhost',
port: 8080
})

response = resource.api_get('/v1/bloop')
assert_equal(resource.timeout, 1989)
assert_equal(response.a, 1)
end
end