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
12 changes: 7 additions & 5 deletions lib/api/helpers/api_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ def has_data?(response)
response.present? && response.body.present?
end

def set_err(json_response)
def set_err(response_code, json_response)
if json_response.present?
if json_response["error"].present?
@error = json_response["error"]
elsif json_response["code"].present?
@error = get_error_message(response.code, json_response["code"])
@error = get_error_message(response_code, json_response["code"])
else
@error = DEFAULT_ERR
end
Expand All @@ -58,13 +58,15 @@ def parse_data(response)
begin
json_response = JSON.parse(response.body)
rescue => e
Rails.logger.error "Failed to parse JSON return from Buffer: #{e}"
msg = "Failed to parse JSON return from Buffer: #{e}"
log_or_print msg
@error = msg
end

if response.code == GOOD_RESPONSE
if response.code == GOOD_RESPONSE || json_response.nil?
return json_response
else
set_err(json_response)
set_err(response.code, json_response)
end
end

Expand Down
8 changes: 7 additions & 1 deletion lib/api/user_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
class UserApi < BaseApi

# PATHS
USER_PATH = "user.json"
USER_PATH = "user.json"
DEAUTH_PATH = "user/deauthorize.json"

def get_user_id
get_user_json["id"] rescue nil
Expand All @@ -13,4 +14,9 @@ def get_user_json
return nil unless verify_token
get_get_response( build_url(USER_PATH) )
end

def deauthorize
return nil unless verify_token
get_get_response( build_url(DEAUTH_PATH) )
end
end
21 changes: 20 additions & 1 deletion lib/buffer_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ def get_user_json
user_json
end

def deauthorize
success_json = @user_api.deauthorize
record_err(@user_api)

is_success?(success_json)
end

###############
# PROFILE API #
###############
Expand All @@ -108,6 +115,7 @@ def get_schedule(id)
end

def update_schedule(id, sched_array)
sched_array.deep_symbolize_keys!
success_json = @profile_api.update_schedule(id, sched_array)
record_err(@profile_api)

Expand Down Expand Up @@ -153,7 +161,14 @@ def reorder_updates(id, updates_array, options = {})
new_order_json = @update_api.reorder_updates(id, updates_array, options)
record_err(@update_api)

new_order_json
extract_key(new_order_json, "updates")
end

def shuffle_updates(id, options = {})
shuffle_json = @update_api.shuffle_updates(id, options)
record_err(@update_api)

extract_key(shuffle_json, "updates")
end

def create_update(profile_ids, options = {})
Expand Down Expand Up @@ -229,4 +244,8 @@ def is_success?(success_json)
def record_err(api)
@error = api.get_error if api.has_error?
end

def extract_key(json, key)
return json[key] if json.present?
end
end
80 changes: 40 additions & 40 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
# Buffer Gem

Modelled after Octokit <3. This is a lightweight client for the Buffer API (https://buffer.com/developers/api), which is intended to return JSON as opposed to objects.
Modelled after [Octokit](https://github.com/octokit/octokit.rb) <3, this is a lightweight client for the [Buffer API](https://buffer.com/developers/api), which is intended to return (mostly) JSON as opposed to objects.

[![Code Climate](https://codeclimate.com/github/kaneda/buffer-ruby/badges/gpa.svg)](https://codeclimate.com/github/kaneda/buffer-ruby)
[![Test Coverage](https://codeclimate.com/github/kaneda/buffer-ruby/badges/coverage.svg)](https://codeclimate.com/github/kaneda/buffer-ruby/coverage)
[![Build Status](https://travis-ci.org/kaneda/buffer-ruby.svg?branch=master)](https://travis-ci.org/kaneda/buffer-ruby)

## Client API

### What's Imlemented?
* https://buffer.com/developers/api/oauth
* https://buffer.com/developers/api/user
* https://buffer.com/developers/api/profiles
* https://buffer.com/developers/api/updates
* https://buffer.com/developers/api/links
* https://buffer.com/developers/api/info
* https://buffer.com/developers/api/errors


### What's Missing?
* Rspecs
* [OAuth](https://buffer.com/developers/api/oauth)
* [Users](https://buffer.com/developers/api/user)
* [Profiles](https://buffer.com/developers/api/profiles)
* [Updates](https://buffer.com/developers/api/updates)
* [Links](https://buffer.com/developers/api/links)
* [Info](https://buffer.com/developers/api/info)
* [Errors](https://buffer.com/developers/api/errors)
* Automated builds through Travis-CI
* Automated tests using RSpec

### Installing

Expand All @@ -45,39 +44,39 @@ You can define the client up front or configure it later. The options array the
buffer_client = BufferClient.new({
:user_code => "yourcode"
})
```

```ruby
buffer_client = BufferClient.new
auth_tok = buffer_client.get_auth_token

buffer_client.configure({
:user_code => "yourcode"
:auth_token => auth_tok
})
```

### Available Calls
| Client call | Input | Description | Notes
| :---------: | :------ | :---- | :---------
| get_auth_token | | Returns the user's long-lasting auth token | user_code must be defined in the buffer_client, as well as the ENV variables "BUFFER_KEY" and "BUFFER_SECRET" |
| get_user_id | | Returns the user's Buffer ID | |
| get_user_json | | Returns the entirety of the user JSON | |
| get_user_profiles | | Returns the entirety of the profile JSON | |
| get_user_profile | Profile ID | Get a single profile as JSON by ID | |
| get_schedule | Profile ID | Get the schedule of a profile as JSON by ID | |
| update_schedule | Profile ID, Schedule Hash | Set the schedule of a profile as JSON by ID | auth_token must be defined in the buffer client. See below for schedule representation |
| get_update | Social Media Post ID | Gets an update by post ID | |
| get_pending_updates | Profile ID, Options Hash (optional) | Gets pending updates as JSON by profile ID | Takes in hash of options, see Buffer API docs for optional parameters |
| get_sent_updates | Profile ID, Options Hash (optional) | Gets sent updates as JSON by profile ID | Takes in hash of options, see Buffer API docs for optional parameters |
| get_interactions | Social Media Post ID, Event, Options Hash (optional) | Gets interactions based on event type (see https://bufferapp.com/developers/api/info#configuration) | Takes in a hash of options, see Buffer API docs for optional parameters |
| reorder_updates | Profile ID, Updates Array, Options Hash (optional) | Updates order of updates in a profile based on updates array | |
| shuffle_updates | Profile ID, Options Hash (optional) | Randomize the order of updates to be sent | |
| create_update | Profile ID Array, Options Hash (optional) | Create a new post | Note that for the "media" option, please specify each media option in the hash separately, e.g. ```{ "media[link]" => "http%3A%2F%2Fgoogle.com", "media[description]" => "The%20google%20homepage" }``` |
| update_status | Social Media Post ID, Text, Options Hash (optional) | Update an existing status | For the "media" option see the note on create_update |
| share_update | Social Media Post ID | Share a post immediately | |
| destroy_update | Social Media Post ID| Permanently destroy an update | |
| move_to_top | Social Media Post ID| Move post to top of queue | |
| get_shares | URL (unencoded) | Gets the number of shares for a given URL through Buffer | You can pass a normal URL here, the client will encode it. This is one of the only calls to not require an auth_token |
| get_configuration | | Gets the current Buffer config | |

| Client call | Input | Output | Notes
| :---------: | :------ | :------ | :---------
| [get_auth_token](https://buffer.com/developers/api/oauth) | | Auth Token:String | ENV variables "BUFFER_KEY" and "BUFFER_SECRET" must be defined here |
| get_user_id | | ID:String | Convenience wrapper, calls get_user_json under the hood |
| [get_user_json](https://buffer.com/developers/api/user#user) | | User:Hash | |
| [deauthorize](https://buffer.com/developers/api/user#deauthorize) | | Success:Boolean | |
| [get_user_profiles](https://buffer.com/developers/api/profiles#profiles) | | Profiles:Array<Hash> | |
| [get_user_profile](https://buffer.com/developers/api/profiles#profilesid) | Profile ID:String | Profile:Hash | |
| [get_schedule](https://buffer.com/developers/api/profiles#schedules) | Profile ID:String | Schedule:Hash | |
| [update_schedule](https://buffer.com/developers/api/profiles#schedulesupdate) | Profile ID:String, Schedule:Hash | Success:Boolean | See below for schedule representation |
| [get_update](https://buffer.com/developers/api/updates#updatesid) | Social Media Post ID:String | Update:Hash | |
| [get_pending_updates](https://buffer.com/developers/api/updates#updatespending) | Profile ID:String, Options:Hash (optional) | UpdateResult:Hash | See Buffer API doc (link to the left) for optional parameters. "updates" key contains the Updates:Array<Hash> |
| [get_sent_updates](https://buffer.com/developers/api/updates#updatessent) | Profile ID:String, Options:Hash (optional) | UpdateResult:Hash | See Buffer API doc (link to the left) for optional parameters. "updates" key contains the Updates:Array<Hash> |
| [get_interactions](https://buffer.com/developers/api/updates#updatesinteractions) | Social Media Post ID:String, Event:ENUM, Options:Hash (optional) | Interactions:Hash | See [event types](https://bufferapp.com/developers/api/info#configuration) for possible event values and Buffer API doc (link to the left) for optional parameters. "interactions" key contains the Interactions:Array<Hash> |
| [reorder_updates](https://buffer.com/developers/api/updates#updatesreorder) | Profile ID:String, Updates:Array, Options:Hash (optional) | Updates:Array<Hash> | |
| [shuffle_updates](https://buffer.com/developers/api/updates#updatesshuffle) | Profile ID:String, Options:Hash (optional) | Updates:Array<Hash> | |
| [create_update](https://buffer.com/developers/api/updates#updatescreate) | Profile IDs:Array, Options:Hash (optional) | Update:Hash | Note that for the "media" option, please specify each media option in the hash separately, e.g. ```{ "media[link]" => "http%3A%2F%2Fgoogle.com", "media[description]" => "The%20google%20homepage" }```. See all available options in the Buffer docs (link to the left) |
| [update_status](https://buffer.com/developers/api/updates#updatesupdate) | Social Media Post ID:String, Text:String, Options:Hash (optional) | Update:Hash | For the "media" option see the note on create_update above |
| [share_update](https://buffer.com/developers/api/updates#updatesshare) | Social Media Post ID:String | Success:Boolean | |
| [destroy_update](https://buffer.com/developers/api/updates#updatesdestroy) | Social Media Post ID:String | Success:Boolean | |
| [move_to_top](https://buffer.com/developers/api/updates#updatesmovetotop) | Social Media Post ID:String | Success:Boolean | |
| [get_shares](https://buffer.com/developers/api/links#shares) | Unencodded URL:String | Shares:Integer | You can pass a normal URL here, the client will encode it. This is one of the only calls to not require an auth_token |
| [get_configuration](https://buffer.com/developers/api/info#configuration) | | Configuration:Hash | "services" key has internal keys for each service |

### Helper methods
| Method | Description |
Expand All @@ -104,20 +103,21 @@ To update a schedule the BufferClient is expecting a schedule of the form:
]
```

Note that you can make the keys symbols or strings, the client will accept either one.
## Contributing

To contribute simply:

1. Fork this project
2. Make your changes in a new branch
3. Create a PR
4. Once approved squash your commits (http://davidwalsh.name/squash-commits-git)
4. Once approved [squash your commits](http://davidwalsh.name/squash-commits-git)
5. Party

## Contact

Email: kanedasan@gmail.com

Twitter: @kanedasan
Twitter: [@kanedasan](https://twitter.com/kanedasan)

IRC: kaneda^ on FreeNode (##hackers)
137 changes: 137 additions & 0 deletions spec/lib/api/helpers/api_helpers_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
require 'action_dispatch'
require 'uri'
require_relative '../../../../lib/api/helpers/api_helpers.rb'

describe ApiHelpers do
let(:base_api) { build(:base_api) }
let(:options) do
{
:key => "value",
:key_2 => "value_2"
}
end

let(:err) { "An error" }

describe "#build_options_string" do
let(:path_regex) do
/\A(\w+=[\w\d]+(&\w+=[\w\d]+)+)*\Z/
end

it "returns a string representing a valid path" do
expect(base_api.build_options_string(options)).to match(path_regex)
end
end

describe "#build_url" do
let(:path) { "some_path" }
let(:auth_tok) { "123456789" }

it "returns a valid URL when given only a path" do
expect(base_api.build_url(path, {}, false)).to match(URI::regexp)
end

it "returns a valid URL when given a path and options" do
expect(base_api.build_url(path, options, false)).to match(URI::regexp)
end

it "returns a valid URL when given a path and an auth token" do
base_api.instance_variable_set(:@auth_token, auth_tok)

expect(base_api.build_url(path)).to match(URI::regexp)
end

it "returns a valid URL when given a path, options, and an auth token" do
base_api.instance_variable_set(:@auth_token, auth_tok)

expect(base_api.build_url(path, options)).to match(URI::regexp)
end
end

describe "#set_err" do
let(:response_code) { "403" }
let(:err) { "An error" }
let(:err_2) { "A second err" }
let(:json_with_error) { { "error" => err } }
let(:json_with_code) { { "code" => "1001" } }
let(:json_with_other) { { "other" => "stuff" } }

it "returns the default error when response is blank" do
expect(base_api.set_err(response_code, {})).to eq(ApiHelpers::DEFAULT_ERR)
end

it "returns an error when the error key is present in the response" do
expect(base_api.set_err(response_code, json_with_error)).to eq(err)
end

it "returns another error when only the code key is present" do
allow_any_instance_of(BaseApi).to receive(:get_error_message).and_return(err_2)
expect(base_api.set_err(response_code, json_with_code)).to eq(err_2)
end

it "returns the default error when response is present but neither error nor code is present" do
expect(base_api.set_err(response_code, json_with_other)).to eq(ApiHelpers::DEFAULT_ERR)
end
end

describe "#has_data?" do
let(:empty_response) do
ActionDispatch::Response.new(500, {}, "")
end

let(:valid_response) do
ActionDispatch::Response.new(200, {}, "A body")
end

it "returns false when passed a blank response" do
expect(base_api.has_data?(empty_response)).to eq(false)
end

it "returns true when passed a response with a body" do
expect(base_api.has_data?(valid_response)).to eq(true)
end
end

describe "#parse_data" do
let(:bad_json) { '{"foo": "bar"' }
let(:bad_json_err) { "Failed to parse JSON return from Buffer" }
let(:bad_response) do
ActionDispatch::Response.new(403, {}, bad_json)
end

let(:good_json) { "#{bad_json} }" }
let(:good_response) do
ActionDispatch::Response.new(200, {}, good_json)
end

it "returns nil and sets @error if the response is invalid JSON" do
json = base_api.parse_data(bad_response)
expect(json).to be_nil
expect(base_api.error).to include(bad_json_err)
end

it "returns a hash when passed good JSON" do
json = base_api.parse_data(good_response)
expect(json).to be_present
expect(json.is_a?(Hash)).to eq(true)
end
end

describe "#get_error_message" do
let(:event_err) { "Event type not supported." }
let(:http_code) { "400" }
let(:err_code) { "1029" }

it "returns the event err when the given codes are passed" do
expect(base_api.get_error_message(http_code, err_code)).to eq(event_err)
end

it "returns the default error when the http code is unknown" do
expect(base_api.get_error_message("10101", err_code)).to eq(ApiHelpers::DEFAULT_ERR)
end

it "returns the default error when the error code is unknown" do
expect(base_api.get_error_message(http_code, "10101")).to eq(ApiHelpers::DEFAULT_ERR)
end
end
end
25 changes: 25 additions & 0 deletions spec/lib/api/update_api_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require_relative '../../../lib/api/update_api.rb'

describe UpdateApi do
let(:update_api) { build(:update_api) }
let(:id) { "123456789" }
let(:path) { "some_path" }

before(:each) do
allow_any_instance_of(UpdateApi).to receive(:verify_token).and_return(true)
end

describe "#build_profile_url" do
it "returns a proper profile URL" do
expected_val = "#{UpdateApi::PROFILE_PATH}/#{id}/#{path}"
expect(update_api.send(:build_profile_url, id, path)).to include(expected_val)
end
end

describe "#build_update_url" do
it "returns a proper update URL" do
expected_val = "#{UpdateApi::UPDATE_PATH}/#{id}/#{path}"
expect(update_api.send(:build_update_url, id, path)).to include(expected_val)
end
end
end