diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..2559e39 --- /dev/null +++ b/.rspec @@ -0,0 +1,3 @@ +--color +--format progress +--require spec_helper diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6aeddbf --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: ruby +rvm: + - "1.9.3" + - "2.1.2" +env: + - TRAVIS_CI=true diff --git a/Gemfile b/Gemfile index 661f721..c9f693b 100644 --- a/Gemfile +++ b/Gemfile @@ -27,11 +27,7 @@ gem 'sdoc', '~> 0.4.0', group: :doc gem 'spring', group: :development # Use ActiveModel has_secure_password -gem 'bcrypt', '~> 3.1.7' - -gem 'buff' - -gem 'omniauth-buffer2' +# gem 'bcrypt', '~> 3.1.7' # Use unicorn as the app server # gem 'unicorn' @@ -42,3 +38,7 @@ gem 'omniauth-buffer2' # Use debugger # gem 'debugger', group: [:development, :test] +gem 'rspec', group: :test +gem 'webmock', group: :test +gem 'factory_girl', group: :test +gem "codeclimate-test-reporter", group: :test, require: nil diff --git a/Gemfile.lock b/Gemfile.lock index af80d08..1a56586 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -29,15 +29,9 @@ GEM tzinfo (~> 1.1) addressable (2.3.8) arel (5.0.1.20140414130214) - bcrypt (3.1.10) - buff (0.0.6) - addressable - faraday - faraday_middleware - multi_json - rash - yajl-ruby builder (3.2.2) + codeclimate-test-reporter (0.4.7) + simplecov (>= 0.7.1, < 1.0.0) coffee-rails (4.0.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) @@ -45,13 +39,14 @@ GEM coffee-script-source execjs coffee-script-source (1.9.1.1) + crack (0.4.2) + safe_yaml (~> 1.0.0) + diff-lcs (1.2.5) + docile (1.1.5) erubis (2.7.0) execjs (2.5.2) - faraday (0.9.1) - multipart-post (>= 1.2, < 3) - faraday_middleware (0.10.0) - faraday (>= 0.7.4, < 0.10) - hashie (2.0.5) + factory_girl (4.5.0) + activesupport (>= 3.0.0) hike (1.2.3) i18n (0.7.0) jbuilder (2.3.1) @@ -61,7 +56,6 @@ GEM railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) json (1.8.3) - jwt (1.5.1) libv8 (3.16.14.3) mail (2.5.4) mime-types (~> 1.16) @@ -69,23 +63,6 @@ GEM mime-types (1.25.1) minitest (5.7.0) multi_json (1.11.2) - multi_xml (0.5.5) - multipart-post (2.0.0) - oauth2 (1.0.0) - faraday (>= 0.8, < 0.10) - jwt (~> 1.0) - multi_json (~> 1.3) - multi_xml (~> 0.5) - rack (~> 1.2) - omniauth (1.2.2) - hashie (>= 1.2, < 4) - rack (~> 1.0) - omniauth-buffer2 (0.0.1) - omniauth (~> 1.0) - omniauth-oauth2 (~> 1.1) - omniauth-oauth2 (1.3.1) - oauth2 (~> 1.0) - omniauth (~> 1.2) pg (0.17.1) polyglot (0.3.5) rack (1.5.5) @@ -107,11 +84,22 @@ GEM rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rake (10.4.2) - rash (0.4.0) - hashie (~> 2.0.0) rdoc (4.2.0) json (~> 1.4) ref (1.0.5) + rspec (3.0.0) + rspec-core (~> 3.0.0) + rspec-expectations (~> 3.0.0) + rspec-mocks (~> 3.0.0) + rspec-core (3.0.4) + rspec-support (~> 3.0.0) + rspec-expectations (3.0.4) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.0.0) + rspec-mocks (3.0.4) + rspec-support (~> 3.0.0) + rspec-support (3.0.4) + safe_yaml (1.0.4) sass (3.2.19) sass-rails (4.0.5) railties (>= 4.0.0, < 5.0) @@ -121,6 +109,11 @@ GEM sdoc (0.4.1) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) + simplecov (0.10.0) + docile (~> 1.1.0) + json (~> 1.8) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.0) spring (1.3.6) sprockets (2.12.4) hike (~> 1.2) @@ -147,23 +140,26 @@ GEM uglifier (2.7.1) execjs (>= 0.3.0) json (>= 1.8.0) - yajl-ruby (1.2.1) + webmock (1.21.0) + addressable (>= 2.3.6) + crack (>= 0.3.2) PLATFORMS ruby DEPENDENCIES - bcrypt (~> 3.1.7) - buff + codeclimate-test-reporter coffee-rails (~> 4.0.0) + factory_girl jbuilder (~> 2.0) jquery-rails - omniauth-buffer2 pg rails (= 4.1.5) + rspec sass-rails (~> 4.0.3) sdoc (~> 0.4.0) spring therubyracer turbolinks uglifier (>= 1.3.0) + webmock diff --git a/Rakefile b/Rakefile index ba6b733..560cc2c 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,19 @@ -# Add your own tasks in files placed in lib/tasks ending in .rake, -# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. +require 'rake' -require File.expand_path('../config/application', __FILE__) +task :console do + require 'irb' + require 'irb/completion' + require 'buffer_app' + ARGV.clear + IRB.start +end -Rails.application.load_tasks +begin + require 'rspec/core/rake_task' + + RSpec::Core::RakeTask.new(:spec) + + task :default => :spec +rescue LoadError + # no rspec available +end diff --git a/lib/api/auth_api.rb b/lib/api/auth_api.rb index 68cb374..568f233 100644 --- a/lib/api/auth_api.rb +++ b/lib/api/auth_api.rb @@ -1,23 +1,27 @@ +require_relative 'base_api.rb' + class AuthApi < BaseApi # PATHS OAUTH_PATH = "oauth2/token.json" - def get_auth_code + def get_auth_token return nil unless verify_user_code && verify_env_vars - oauth_url = "#{API_URL}/#{API_VERSION}/#{OAUTH_PATH}" - uri = URI.parse(oauth_url) + oauth_url = build_url(OAUTH_PATH, {}, false) post_data = "client_id=#{ENV['BUFFER_KEY']}&" + "client_secret=#{ENV['BUFFER_SECRET']}&" + - "redirect_uri=#{Rails.configuration.buffer_redirect_uri}&" + + "redirect_uri=#{ENV['REDIRECT_URI']}&" + "code=#{@user_code}&" + "grant_type=authorization_code" - json_response = get_post_response(uri, post_data) - if json_response.present? + json_response = get_post_response(oauth_url, post_data) + if json_response.present? && json_response["access_token"].present? json_response["access_token"] + else + @error = "Failed to get JSON token, received: #{json_response}" + nil end end end diff --git a/lib/api/base_api.rb b/lib/api/base_api.rb index 040250c..e01f1c2 100644 --- a/lib/api/base_api.rb +++ b/lib/api/base_api.rb @@ -2,11 +2,9 @@ require "net/http" require "uri" require_relative "helpers/api_helpers.rb" -require_relative "../buffer_vars.rb" class BaseApi include ApiHelpers - include BufferVars def initialize(options = {}) @user_code = options[:user_code] @@ -21,6 +19,16 @@ def configure(options = {}) @logger = options[:logger] if options[:logger] end + def has_error? + @error.present? + end + + def get_error + tmp_error = @error + @error = nil + tmp_error + end + def error @error end diff --git a/lib/api/helpers/api_helpers.rb b/lib/api/helpers/api_helpers.rb index 167d3f5..b8e72e0 100644 --- a/lib/api/helpers/api_helpers.rb +++ b/lib/api/helpers/api_helpers.rb @@ -1,6 +1,9 @@ require_relative '../error_codes.rb' +require_relative '../../buffer_vars.rb' module ApiHelpers + include BufferVars + GOOD_RESPONSE = "200" DEFAULT_ERR = "An unknown error occurred, please contact an administrator for assistance" @@ -12,60 +15,100 @@ def log_or_print(msg, method = :error) end end + def build_options_string(options = {}) + options.collect { |k, v| "#{k}=#{v}" }.join("&") + end + + def build_url(path, options = {}, auth_token = true) + url = "#{API_URL}/#{API_VERSION}/#{path}" + options_string = build_options_string(options) + token_param = "access_token=#{@auth_token}" + + if options_string.present? + url += "?#{options_string}" + url += "&#{token_param}" if auth_token + elsif auth_token + url += "?#{token_param}" + end + + url + end + def has_data?(response) response.present? && response.body.present? end + 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"]) + else + @error = DEFAULT_ERR + end + else + @error = DEFAULT_ERR + end + end + def parse_data(response) + return nil unless has_data?(response) + begin - JSON.parse(response.body) + json_response = JSON.parse(response.body) rescue => e - Rails.logger.error "Failed to parse JSON return from Buffer: #{e}" - nil + msg = "Failed to parse JSON return from Buffer: #{e}" + log_or_print msg + @error = msg + end + + if response.code == GOOD_RESPONSE || json_response.nil? + return json_response + else + set_err(response.code, json_response) end end - def get_http_obj(uri) - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true + def get_http_obj(url) + uri = URI.parse(url) + raise URI::InvalidURIError if uri.host.nil? + + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE - http + [ uri, http ] end - def get_get_response(uri) - http = get_http_obj(uri) + def get_get_response(url) + uri, http = get_http_obj(url) - req = Net::HTTP::Get.new(uri) + req = begin + Net::HTTP::Get.new(uri) + rescue + # Fall back in case of Ruby 1.9.3 + Net::HTTP::Get.new(uri.request_uri) + end response = http.request(req) - if has_data?(response) - parse_data(response) - end + parse_data(response) end - def get_post_response(uri, post_data) - http = get_http_obj(uri) + def get_post_response(url, post_data = "") + uri, http = get_http_obj(url) - req = Net::HTTP::Post.new(uri) + req = begin + Net::HTTP::Post.new(uri) + rescue + # Fall back in case of Ruby 1.9.3 + Net::HTTP::Post.new(uri.request_uri) + end req.content_type = "application/x-www-form-urlencoded" req.body = post_data response = http.request(req) - if has_data?(response) - json_response = parse_data(response) - if json_response.present? - if response.code == GOOD_RESPONSE - return json_response["access_token"] - elsif json_response["error"].present? - @error = json_response["error"] - elsif json_response["error_code"].present? - @error = get_error_message(response.code, json_response["error_code"]) - else - @error = DEFAULT_ERR - end - end - end + parse_data(response) end def get_error_message(http_code, error_code) diff --git a/lib/api/info_api.rb b/lib/api/info_api.rb new file mode 100644 index 0000000..7aac66a --- /dev/null +++ b/lib/api/info_api.rb @@ -0,0 +1,12 @@ +require_relative 'base_api.rb' + +class InfoApi < BaseApi + + # PATHS + CONFIG_PATH = "info/configuration.json" + + def get_configuration + return nil unless verify_token + get_get_response( build_url(CONFIG_PATH) ) + end +end diff --git a/lib/api/link_api.rb b/lib/api/link_api.rb new file mode 100644 index 0000000..5609a58 --- /dev/null +++ b/lib/api/link_api.rb @@ -0,0 +1,11 @@ +require_relative 'base_api.rb' + +class LinkApi < BaseApi + + # PATHS + SHARES_PATH = "links/shares.json" + + def get_shares(encoded_url) + get_get_response( build_url("#{SHARES_PATH}?url=#{encoded_url}", {}, false) ) + end +end diff --git a/lib/api/profile_api.rb b/lib/api/profile_api.rb index 84e7cf5..67a2826 100644 --- a/lib/api/profile_api.rb +++ b/lib/api/profile_api.rb @@ -1,14 +1,44 @@ +require_relative 'base_api.rb' + class ProfileApi < BaseApi # PATHS - PROFILES_PATH = "profiles.json" + PROFILE_PATH = "profiles" + PROFILES_PATH = "#{PROFILE_PATH}.json" + SCHEDULE_PATH = "schedules" + SCHEDULES_PATH = "#{SCHEDULE_PATH}.json" + UPDATE_PATH = "#{SCHEDULE_PATH}/update.json" def get_profiles return nil unless verify_token + get_get_response( build_url(PROFILES_PATH) ) + end + + def get_profile(id) + return nil unless verify_token + get_get_response( build_url("#{PROFILE_PATH}/#{id}.json") ) + end + + def get_schedule(id) + return nil unless verify_token + get_get_response( build_url("#{PROFILE_PATH}/#{id}/#{SCHEDULES_PATH}") ) + end + + def update_schedule(id, sched_array) + return nil unless verify_token + + update_url = build_url("#{PROFILE_PATH}/#{id}/#{UPDATE_PATH}") - profile_url = "#{API_URL}/#{API_VERSION}/#{PROFILES_PATH}?access_token=#{@auth_code}" - uri = URI.parse(profile_url) + post_data = "" + sched_array.each_with_index do |sched, index| + base = "schedules[#{index}]" + [:days, :times].each do |key| + sched[key].each do |val| + post_data += "#{base}[#{key}][]=#{val}&" + end + end + end - get_get_response(uri) + get_post_response(update_url, post_data) end end diff --git a/lib/api/update_api.rb b/lib/api/update_api.rb new file mode 100644 index 0000000..0a1ccd1 --- /dev/null +++ b/lib/api/update_api.rb @@ -0,0 +1,93 @@ +require_relative 'base_api.rb' + +class UpdateApi < BaseApi + + # PATHS + PROFILE_PATH = "profiles" + UPDATE_PATH = "updates" + PENDING_PATH = "#{UPDATE_PATH}/pending.json" + SENT_PATH = "#{UPDATE_PATH}/sent.json" + INTERACTIONS_PATH = "interactions.json" + REORDER_PATH = "#{UPDATE_PATH}/reorder.json" + SHUFFLE_PATH = "#{UPDATE_PATH}/shuffle.json" + CREATE_PATH = "#{UPDATE_PATH}/create.json" + UPDATE_STATUS_PATH = "update.json" + SHARE_PATH = "share.json" + DESTROY_PATH = "destroy.json" + MOVE_TOP_PATH = "move_to_top.json" + + def get_update(id) + return nil unless verify_token + get_get_response( build_url("#{UPDATE_PATH}/#{id}.json") ) + end + + def get_pending_updates(id, options = {}) + return nil unless verify_token + get_get_response( build_profile_url(id, PENDING_PATH, options) ) + end + + def get_sent_updates(id, options = {}) + return nil unless verify_token + get_get_response( build_profile_url(id, SENT_PATH, options) ) + end + + def get_interactions(id, options = {}) + return nil unless verify_token + get_get_response( build_update_url(id, INTERACTIONS_PATH, options) ) + end + + def reorder_updates(id, updates_array, options = {}) + return nil unless verify_token + post_data = "#{build_options_string(options)}&" + post_data += updates_array.map { |u| "order[]=#{u}" }.join("&") + + get_post_response( build_profile_url(id, REORDER_PATH), post_data ) + end + + def shuffle_updates(id, options = {}) + return nil unless verify_token + post_data = build_options_string(options) + + get_post_response( build_profile_url(id, SHUFFLE_PATH), post_data ) + end + + def create_update(profile_ids, options = {}) + return nil unless verify_token + post_data = "#{build_options_string(options)}&" + post_data += profile_ids.map { |i| "profile_ids[]=#{i}" }.join("&") + + get_post_response( build_url(CREATE_PATH), post_data ) + end + + def update_status(id, options = {}) + return nil unless verify_token + post_data = build_options_string(options) + + get_post_response( build_update_url(id, UPDATE_STATUS_PATH), post_data ) + end + + def share_update(id) + return nil unless verify_token + get_post_response( build_update_url(id, SHARE_PATH) ) + end + + def destroy_update(id) + return nil unless verify_token + get_post_response( build_update_url(id, DESTROY_PATH) ) + end + + def move_to_top(id) + return nil unless verify_token + get_post_response( build_update_url(id, MOVE_TOP_PATH) ) + end + + private + + def build_update_url(id, path, options = {}) + build_url("#{UPDATE_PATH}/#{id}/#{path}", options) + end + + def build_profile_url(id, path, options = {}) + build_url("#{PROFILE_PATH}/#{id}/#{path}", options) + end +end diff --git a/lib/api/user_api.rb b/lib/api/user_api.rb index af5923b..2f420ca 100644 --- a/lib/api/user_api.rb +++ b/lib/api/user_api.rb @@ -1,7 +1,10 @@ +require_relative 'base_api.rb' + 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 @@ -9,10 +12,11 @@ def get_user_id def get_user_json return nil unless verify_token + get_get_response( build_url(USER_PATH) ) + end - user_url = "#{API_URL}/#{API_VERSION}/#{USER_PATH}?access_token=#{@auth_token}" - uri = URI.parse(user_url) - - get_get_response(uri) + def deauthorize + return nil unless verify_token + get_get_response( build_url(DEAUTH_PATH) ) end end diff --git a/lib/buffer_client.rb b/lib/buffer_client.rb index d2b0106..ec5d2b8 100644 --- a/lib/buffer_client.rb +++ b/lib/buffer_client.rb @@ -1,8 +1,10 @@ -require "net/http" -require "uri" +require 'cgi' require_relative "api/auth_api.rb" require_relative "api/user_api.rb" require_relative "api/profile_api.rb" +require_relative "api/update_api.rb" +require_relative "api/link_api.rb" +require_relative "api/info_api.rb" class BufferClient def initialize(options = {}) @@ -11,26 +13,239 @@ def initialize(options = {}) # Initialize the API objects we may use @auth_api = AuthApi.new(options) @user_api = UserApi.new(options) - @profile_api = ProflieApi.new(options) + @profile_api = ProfileApi.new(options) + @update_api = UpdateApi.new(options) + @link_api = LinkApi.new(options) + @info_api = InfoApi.new(options) + + @api_objects = [ + @auth_api, + @user_api, + @profile_api, + @update_api, + @link_api, + @info_api + ] + end + + ################## + # HELPER METHODS # + ################## + + # Reconfigure API objects + def configure(options = {}) + @api_objects.each do |api| + api.configure(options) + end + end + + def has_error? + @error.present? + end + + def get_error + tmp_error = @error + @error = nil + tmp_error end def error @error end + ############ + # AUTH API # + ############ + def get_auth_token - AuthApi.get_auth_token + token = @auth_api.get_auth_token + record_err(@auth_api) + + token end + ############ + # USER API # + ############ + def get_user_id - UserApi.get_user_id + user_id = @user_api.get_user_id + record_err(@user_api) + + user_id end def get_user_json - UserApi.get_user_json + user_json = @user_api.get_user_json + record_err(@user_api) + + user_json + end + + def deauthorize + success_json = @user_api.deauthorize + record_err(@user_api) + + is_success?(success_json) end + ############### + # PROFILE API # + ############### + def get_user_profiles - ProfileApi.get_profiles + profiles = @profile_api.get_profiles + record_err(@profile_api) + + profiles + end + + def get_user_profile(id) + profile = @profile_api.get_profile(id) + record_err(@profile_api) + + profile + end + + def get_schedule(id) + schedule = @profile_api.get_schedule(id) + record_err(@profile_api) + + schedule + 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) + + is_success?(success_json) + end + + ############## + # UPDATE API # + ############## + + def get_update(id) + update_json = @update_api.get_update(id) + record_err(@update_api) + + update_json + end + + def get_pending_updates(id, options = {}) + pending_updates_json = @update_api.get_pending_updates(id, options) + record_err(@update_api) + + pending_updates_json + end + + def get_sent_updates(id, options = {}) + sent_updates_json = @update_api.get_sent_updates(id, options) + record_err(@update_api) + + sent_updates_json + end + + def get_interactions(id, event, options = {}) + # Forces the user to enter a value for event without + # needing to validate the hash + options[:event] = event + interactions_json = @update_api.get_interactions(id, options) + record_err(@update_api) + + interactions_json + end + + def reorder_updates(id, updates_array, options = {}) + new_order_json = @update_api.reorder_updates(id, updates_array, options) + record_err(@update_api) + + 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 = {}) + post_json = @update_api.create_update(profile_ids, options) + record_err(@update_api) + + post_json + end + + def update_status(id, text, options = {}) + # Forces the user to enter a value for text without + # needing to validate the hash + options[:text] = text + update_json = @update_api.update_status(id, options) + record_err(@update_api) + + update_json + end + + def share_update(id) + success_json = @update_api.share_update(id) + record_err(@update_api) + + is_success?(success_json) + end + + def destroy_update(id) + success_json = @update_api.destroy_update(id) + record_err(@update_api) + + is_success?(success_json) + end + + def move_to_top(id) + success_json = @update_api.move_to_top(id) + record_err(@update_api) + + is_success?(success_json) + end + + ############ + # LINK API # + ############ + + def get_shares(url) + # Encode URL + encoded_url = CGI.escape(url) + share_json = @link_api.get_shares(encoded_url) + record_err(@link_api) + + if share_json.present? && share_json["shares"].present? + share_json["shares"] + end + end + + ############ + # INFO API # + ############ + + def get_configuration + info_json = @info_api.get_configuration + record_err(@info_api) + + info_json + end + + private + + def is_success?(success_json) + success_json.present? && success_json.include?("success") && success_json["success"] == true + end + + 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 diff --git a/readme.md b/readme.md index dcc09f0..f0ecde3 100644 --- a/readme.md +++ b/readme.md @@ -1,30 +1,56 @@ # 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. -## Client API +[![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) -### 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/errors +## Table of Contents + +* [Ruby Versions](https://github.com/kaneda/buffer-ruby#ruby-versions) +* [Client API](https://github.com/kaneda/buffer-ruby#client-api) + * [What's Implemented](https://github.com/kaneda/buffer-ruby#whats-imlemented) + * [Installing](https://github.com/kaneda/buffer-ruby#installing) + * [Basic Usage](https://github.com/kaneda/buffer-ruby#basic-usage) + * [Available Calls](https://github.com/kaneda/buffer-ruby#available-calls) + * [Helper Methods](https://github.com/kaneda/buffer-ruby#helper-methods) + * [Defining a Schedule](https://github.com/kaneda/buffer-ruby#defining-a-schedule) +* [Contributing](https://github.com/kaneda/buffer-ruby#contributing) +* [Contact](https://github.com/kaneda/buffer-ruby#contact) + +## Ruby Versions + +This gem is built to work with Ruby 1.9.3+ +See the Travis-CI build information above for build status on 1.9.3 and 2.1.2 -### What's Missing? -* https://buffer.com/developers/api/updates -* https://buffer.com/developers/api/links -* https://buffer.com/developers/api/info +## Client API -### Gemfile +### What's Imlemented? +* [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 Put this sucker in your Gemfile and bundle install ```ruby -gem "buffer-app", :git => "git://github.com/kaneda/buffer-ruby", :ref => "cd461c8a9f934c9a499e7bfe80f93d22e0f6481a" +gem "buffer-app", :git => "git://github.com/kaneda/buffer-ruby", :tag => "v1.2" ``` -Apologies for the SHA, I'll add a tag shortly +Then drop this into your config/application.rb (or wherever you want to use it) + +```ruby +require 'buffer_app' +``` ### Basic Usage @@ -37,23 +63,68 @@ 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 }) + +# Subsequent calls here (see below) ``` ### Available Calls -| Client call | 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 | auth_token must be defined in the buffer client | -| get_user_json | Returns the entirety of the user JSON | auth_token must be defined in the buffer client | -| get_user_profiles | Returns the entirety of the profile JSON | auth_token must be defined in the buffer client | + +| 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 | | +| [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 | +| [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 | +| [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 | +| [reorder_updates](https://buffer.com/developers/api/updates#updatesreorder) | Profile ID:String, Updates:Array, Options:Hash (optional) | Updates:Array | | +| [shuffle_updates](https://buffer.com/developers/api/updates#updatesshuffle) | Profile ID:String, Options:Hash (optional) | Updates:Array | | +| [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 | +| :---------: | :----- | +| configure | Takes in a hash (as above) and reconfigures all API objects | +| has_error? | Returns true if an error has been set in the client | +| get_error | Returns the current error and wipes it out in the client | +| error | Returns the current error, leaving it in tact | + + +### Defining a Schedule + +To update a schedule the BufferClient is expecting a schedule of the form: + +```ruby +[ + { + :days => ['mon', 'tue', 'thu'], + :times => ['12:45', '15:30', '17:43'] + }, + . + . + . +] +``` + +Note that you can make the keys symbols or strings, the client will accept either one. ## Contributing @@ -62,13 +133,13 @@ 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 +## Contact Me Email: kanedasan@gmail.com -Twitter: @kanedasan +Twitter: [@kanedasan](https://twitter.com/kanedasan) IRC: kaneda^ on FreeNode (##hackers) diff --git a/spec/factories.rb b/spec/factories.rb new file mode 100644 index 0000000..ccc6496 --- /dev/null +++ b/spec/factories.rb @@ -0,0 +1,49 @@ +require_relative '../lib/api/base_api.rb' +require_relative '../lib/api/auth_api.rb' +require_relative '../lib/api/user_api.rb' +require_relative '../lib/api/profile_api.rb' +require_relative '../lib/api/update_api.rb' +require_relative '../lib/api/link_api.rb' +require_relative '../lib/api/info_api.rb' + +FactoryGirl.define do + trait :api_traits do + user_code "123456789" + auth_token "123456789abcd" + end + + factory :base_api, traits: [:api_traits] do + skip_create + initialize_with { new(attributes) } + end + + factory :auth_api, traits: [:api_traits] do + skip_create + initialize_with { new(attributes) } + end + + factory :user_api, traits: [:api_traits] do + skip_create + initialize_with { new(attributes) } + end + + factory :profile_api, traits: [:api_traits] do + skip_create + initialize_with { new(attributes) } + end + + factory :update_api, traits: [:api_traits] do + skip_create + initialize_with { new(attributes) } + end + + factory :link_api, traits: [:api_traits] do + skip_create + initialize_with { new(attributes) } + end + + factory :info_api, traits: [:api_traits] do + skip_create + initialize_with { new(attributes) } + end +end diff --git a/spec/lib/api/auth_api_spec.rb b/spec/lib/api/auth_api_spec.rb new file mode 100644 index 0000000..d57d7f6 --- /dev/null +++ b/spec/lib/api/auth_api_spec.rb @@ -0,0 +1,24 @@ +require_relative '../../../lib/api/auth_api.rb' + +describe AuthApi do + let(:auth_api) { build(:auth_api) } + let(:token) { "123456789" } + let(:good_json) { {"access_token" => token} } + + before(:each) do + allow_any_instance_of(AuthApi).to receive(:verify_user_code).and_return(true) + allow_any_instance_of(AuthApi).to receive(:verify_env_vars).and_return(true) + end + + describe "#get_auth_token" do + it "returns the access token with a good JSON response" do + allow_any_instance_of(AuthApi).to receive(:get_post_response).and_return(good_json) + expect(auth_api.get_auth_token).to eq(token) + end + + it "returns nil with bad JSON response" do + allow_any_instance_of(AuthApi).to receive(:get_post_response).and_return(nil) + expect(auth_api.get_auth_token).to be_nil + end + end +end diff --git a/spec/lib/api/base_api_spec.rb b/spec/lib/api/base_api_spec.rb new file mode 100644 index 0000000..c127a8a --- /dev/null +++ b/spec/lib/api/base_api_spec.rb @@ -0,0 +1,97 @@ +require_relative '../../../lib/api/base_api.rb' + +describe BaseApi do + let(:base_api) { build(:base_api) } + let(:err) { "An error" } + + describe "#configure" do + let(:new_user_code) { "987654321" } + let(:new_auth_token) { "abc98764321" } + + it "updates the user code" do + base_api.configure({:user_code => new_user_code}) + expect(base_api.instance_variable_get(:@user_code)).to eql(new_user_code) + end + + it "updates the auth token" do + base_api.configure({:auth_token => new_auth_token}) + expect(base_api.instance_variable_get(:@auth_token)).to eql(new_auth_token) + end + + it "ignores updates that are nil or blank" do + base_api.configure({:auth_token => "", :user_code => ""}) + expect(base_api.instance_variable_get(:@auth_token)).to_not eq(new_auth_token) + expect(base_api.instance_variable_get(:@user_code)).to_not eq(new_user_code) + end + end + + describe "#has_error?" do + it "returns false when error is nil or empty" do + expect(base_api.has_error?).to eq(false) + end + + it "returns true when error is present" do + base_api.instance_variable_set(:@error, err) + expect(base_api.has_error?).to eq(true) + end + end + + describe "#get_error" do + before(:each) do + base_api.instance_variable_set(:@error, err) + end + + it "returns the error" do + expect(base_api.get_error).to eq(err) + end + + it "wipes the error" do + base_api.get_error + + expect(base_api.error).to be_nil + end + end + + describe "#verify_token" do + it "returns true when auth token is present" do + expect(base_api.send(:verify_token)).to eq(true) + end + + it "returns false when auth token is nil or empty" do + base_api.instance_variable_set(:@auth_token, nil) + expect(base_api.send(:verify_token)).to eq(false) + end + end + + describe "#verify_user_code" do + it "returns true when user_code is present" do + expect(base_api.send(:verify_user_code)).to eq(true) + end + + it "returns false when user_code is nil or empty" do + base_api.instance_variable_set(:@user_code, nil) + expect(base_api.send(:verify_user_code)).to eq(false) + end + end + + describe "#verify_env_vars" do + before(:each) do + ENV["BUFFER_KEY"] = "a value" + ENV["BUFFER_SECRET"] = "a value" + end + + it "returns true when all env vars are present" do + expect(base_api.send(:verify_env_vars)).to eq(true) + end + + it "returns false when BUFFER_KEY is nil or empty" do + ENV["BUFFER_KEY"] = nil + expect(base_api.send(:verify_env_vars)).to eq(false) + end + + it "returns false when BUFFER_SECRET is nil or empty" do + ENV["BUFFER_SECRET"] = nil + expect(base_api.send(:verify_env_vars)).to eq(false) + end + end +end diff --git a/spec/lib/api/helpers/api_helpers_spec.rb b/spec/lib/api/helpers/api_helpers_spec.rb new file mode 100644 index 0000000..3a999cf --- /dev/null +++ b/spec/lib/api/helpers/api_helpers_spec.rb @@ -0,0 +1,200 @@ +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(:good_hash) { options } + + let(:err) { "An error" } + let(:good_url) { "http://jbegleiter.com" } + let(:bad_json) { '{"foo": "bar"' } + let(:good_json) { "#{bad_json} }" } + + 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_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_err) { "Failed to parse JSON return from Buffer" } + let(:bad_response) do + ActionDispatch::Response.new(403, {}, bad_json) + end + + 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 + + describe "#get_http_obj" do + let(:bad_url) { "foo" } + + it "raises an error when passed an invalid URL" do + expect { base_api.get_http_obj(bad_url) }.to raise_error + end + + it "returns an array of URI, HTTP object when passed a valid URL" do + uri, http = base_api.get_http_obj(good_url) + expect(uri).to be_present + expect(http).to be_present + end + end + + # Test that these don't blow up keeping in mind that + # get_http_obj and parse_data are tested above + describe "#get_functions" do + before(:each) do + # Stubbed, as this is tested above + allow_any_instance_of(BaseApi).to receive(:parse_data).and_return(good_hash) + + # Since we're stubbing parse it doesn't matter what this returns + allow_any_instance_of(Net::HTTP).to receive(:request).and_return({}) + end + + describe "#get_post_response" do + let(:post_data) { "foo=bar&boz=bot" } + + it "doesn't explode when post data is set" do + expect(base_api.get_post_response(good_url, post_data)).to eq(good_hash) + end + + it "doesn't explode when post data isn't set" do + expect(base_api.get_post_response(good_url)).to eq(good_hash) + end + end + + describe "#get_get_response" do + it "doesn't explode" do + expect(base_api.get_get_response(good_url)).to eq(good_hash) + end + end + end + + describe "#log_or_print" do + let(:msg) { "A message" } + let(:log_level) { "info" } + + it "prints to the logger when logger is defined" do + logger = Logger.new(STDOUT) + base_api.instance_variable_set(:@logger, logger) + + expect(logger).to receive(:send).with(log_level, msg) + base_api.log_or_print(msg, log_level) + end + + it "prints to STDOUT when logger is not defined" do + expect { base_api.log_or_print(msg, log_level) }.to output(/#{msg}(\n)?/).to_stdout + end + end +end diff --git a/spec/lib/api/info_api_spec.rb b/spec/lib/api/info_api_spec.rb new file mode 100644 index 0000000..cf185ca --- /dev/null +++ b/spec/lib/api/info_api_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../../lib/api/info_api.rb' + +describe InfoApi do + let(:info_api) { build(:info_api) } + let(:success_json) { {"info" => "info_and_stuff"} } + + before(:each) do + allow_any_instance_of(InfoApi).to receive(:verify_token).and_return(true) + allow_any_instance_of(InfoApi).to receive(:get_get_response).and_return(success_json) + end + + # Make sure this doesn't blow up, keeping in mind that + # that get_get_response is tested in the ApiHelpers spec + describe "#get_configuration" do + it "returns configuration JSON" do + expect(info_api.get_configuration).to eq(success_json) + end + end +end diff --git a/spec/lib/api/link_api_spec.rb b/spec/lib/api/link_api_spec.rb new file mode 100644 index 0000000..6f27642 --- /dev/null +++ b/spec/lib/api/link_api_spec.rb @@ -0,0 +1,19 @@ +require_relative '../../../lib/api/link_api.rb' + +describe LinkApi do + let(:link_api) { build(:link_api) } + let(:success_json) { {"shares" => 123456} } + let(:url) { "http%3A%2F%2Fjbegleiter.com" } + + before(:each) do + allow_any_instance_of(LinkApi).to receive(:get_get_response).and_return(success_json) + end + + # Make sure this doesn't blow up, keeping in mind that + # that get_get_response is tested in the ApiHelpers spec + describe "#get_shares" do + it "returns share JSON" do + expect(link_api.get_shares(url)).to eq(success_json) + end + end +end diff --git a/spec/lib/api/profile_api_spec.rb b/spec/lib/api/profile_api_spec.rb new file mode 100644 index 0000000..c3f2968 --- /dev/null +++ b/spec/lib/api/profile_api_spec.rb @@ -0,0 +1,66 @@ +require_relative '../../../lib/api/profile_api.rb' + +describe ProfileApi do + let(:profile_api) { build(:profile_api) } + let(:success_json) { {"success" => "true"} } + let(:id) { "123456789" } + let(:days) { ["mon", "tue"] } + let(:good_sched) do + [{ + :days => days, + :times => ["00:01", "12:23"] + }] + end + + let(:bad_sched) do + [{ + :days => days + }] + end + + before(:each) do + allow_any_instance_of(ProfileApi).to receive(:verify_token).and_return(true) + end + + describe "#update_schedule" do + before(:each) do + allow_any_instance_of(ProfileApi).to receive(:get_post_response).and_return(success_json) + end + + it "raises an exception when the schedule is improperly defined" do + expect { profile_api.update_schedule(id, bad_sched) }.to raise_error + end + + it "returns succes with JSON response when schedule is properly defined" do + expect(profile_api.update_schedule(id, good_sched)).to eq(success_json) + end + end + + # Make sure these don't blow up, keeping in mind that + # that get_get_response is tested in the ApiHelpers spec + describe "#gets" do + let(:id) { "123456789" } + + before(:each) do + allow_any_instance_of(ProfileApi).to receive(:get_get_response).and_return(success_json) + end + + describe "#get_profiles" do + it "doesn't blow up on invocation" do + expect(profile_api.get_profiles).to eq(success_json) + end + end + + describe "#get_profile" do + it "doesn't blow up on invocation" do + expect(profile_api.get_profile(id)).to eq(success_json) + end + end + + describe "#get_schedule" do + it "doesn't blow up on invocation" do + expect(profile_api.get_schedule(id)).to eq(success_json) + end + end + end +end diff --git a/spec/lib/api/update_api_spec.rb b/spec/lib/api/update_api_spec.rb new file mode 100644 index 0000000..0858a81 --- /dev/null +++ b/spec/lib/api/update_api_spec.rb @@ -0,0 +1,110 @@ +require_relative '../../../lib/api/update_api.rb' + +describe UpdateApi do + let(:update_api) { build(:update_api) } + let(:id) { "123456789" } + let(:path) { "some_path" } + let(:return_json) { { :key => "value" } } + + 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 + + # Make sure these don't blow up, keeping in mind that + # that get_get_response is tested in the ApiHelpers spec + describe "#gets" do + before(:each) do + allow_any_instance_of(UpdateApi).to receive(:get_get_response).and_return(return_json) + end + + describe "#get_update" do + it "doesn't blow up when invoked" do + expect(update_api.get_update(id)).to eq(return_json) + end + end + + describe "#get_pending_updates" do + it "doesn't blow up when invoked" do + expect(update_api.get_pending_updates(id)).to eq(return_json) + end + end + + describe "#get_sent_updates" do + it "doesn't blow up when invoked" do + expect(update_api.get_sent_updates(id)).to eq(return_json) + end + end + + describe "#get_interactions" do + it "doesn't blow up when invoked" do + expect(update_api.get_interactions(id)).to eq(return_json) + end + end + end + + describe "#posts" do + before(:each) do + allow_any_instance_of(UpdateApi).to receive(:get_post_response).and_return(return_json) + end + + describe "#reorder_updates" do + let(:updates_array) { [ id, id, id ] } + + it "doesn't blow up when invoked" do + expect(update_api.reorder_updates(id, updates_array)).to eq(return_json) + end + end + + describe "#shuffle_updates" do + it "doesn't blow up when invoked" do + expect(update_api.shuffle_updates(id)).to eq(return_json) + end + end + + describe "#create_update" do + let(:profile_ids) { [ id, id, id ] } + + it "doesn't blow up when invoked" do + expect(update_api.create_update(profile_ids)).to eq(return_json) + end + end + + describe "#update_status" do + it "doesn't blow up when invoked" do + expect(update_api.update_status(id)).to eq(return_json) + end + end + + describe "#share_update" do + it "doesn't blow up when invoked" do + expect(update_api.share_update(id)).to eq(return_json) + end + end + + describe "#destroy_update" do + it "doesn't blow up when invoked" do + expect(update_api.destroy_update(id)).to eq(return_json) + end + end + + describe "#move_to_top" do + it "doesn't blow up when invoked" do + expect(update_api.move_to_top(id)).to eq(return_json) + end + end + end +end diff --git a/spec/lib/api/user_api_spec.rb b/spec/lib/api/user_api_spec.rb new file mode 100644 index 0000000..fc3e7fa --- /dev/null +++ b/spec/lib/api/user_api_spec.rb @@ -0,0 +1,25 @@ +require_relative '../../../lib/api/user_api.rb' + +describe UserApi do + let(:user_api) { build(:user_api) } + let(:success_json) { {"key" => "value"} } + + before(:each) do + allow_any_instance_of(UserApi).to receive(:verify_token).and_return(true) + allow_any_instance_of(UserApi).to receive(:get_get_response).and_return(success_json) + end + + # Make sure these don't blow up, keeping in mind that + # that get_get_response is tested in the ApiHelpers spec + describe "#get_user_json" do + it "returns user JSON" do + expect(user_api.get_user_json).to eq(success_json) + end + end + + describe "#deauthorize" do + it "returns success JSON" do + expect(user_api.deauthorize).to eq(success_json) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..bc360b2 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,23 @@ +require "codeclimate-test-reporter" +CodeClimate::TestReporter.start + +require 'webmock/rspec' +require 'factory_girl' + +RSpec.configure do |config| + WebMock.disable_net_connect!(:allow => "codeclimate.com") + + config.order = 'random' + + # FactoryGirl + FactoryGirl.find_definitions + config.include FactoryGirl::Syntax::Methods + + config.before(:suite) do + begin + FactoryGirl.lint + ensure + # + end + end +end