Skip to content

Commit

Permalink
Merge pull request #6 from DeweyLabs/detect-301
Browse files Browse the repository at this point in the history
Raise Faraday errors
  • Loading branch information
ScotterC authored Nov 22, 2024
2 parents 8597610 + 7d3afcb commit 53e4ddd
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 58 deletions.
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ source "https://rubygems.org"
gemspec

gem "standard", "~> 1.33"

gem "debug", "~> 1.9"
2 changes: 1 addition & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ require "rspec/core/rake_task"

RSpec::Core::RakeTask.new(:spec)

task :default => :spec
task default: :spec
7 changes: 5 additions & 2 deletions lib/wp_api_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@
require "wp_api_client/connection"
require "wp_api_client/collection"
require "wp_api_client/relationship"
require "wp_api_client/utils"

module WpApiClient
class RelationNotDefined < StandardError; end

class ErrorResponse < StandardError
attr_reader :error
attr_reader :status
attr_reader :data

def initialize(json)
@error = OpenStruct.new(json)
@status = @error.data["status"]
error_data = WpApiClient::Utils.deep_symbolize(json)
@error = OpenStruct.new(error_data)
@status = @error.data[:status]
super(@error.message)
end
end
Expand Down
8 changes: 6 additions & 2 deletions lib/wp_api_client/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ def each(&block)
end

def next_page
@links[:next] && @links[:next]
@links[:next]
end

def previous_page
@links[:prev] && @links[:prev]
@links[:prev]
end

def method_missing(method, *)
@resources.send(method, *)
end

def respond_to_missing?(method, include_private = false)
@resources.respond_to?(method, include_private) || super
end

private

# https://www.snip2code.com/Snippet/71914/Parse-link-headers-from-Github-API-in-Ru
Expand Down
6 changes: 1 addition & 5 deletions lib/wp_api_client/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,7 @@ def basic_auth=(hash)
@basic_auth = if hash.nil?
nil
else
# Symbolizes one level of keys
hash.each_with_object({}) do |(key, value), result|
symbol_key = key.to_sym
result[symbol_key] = value
end
WpApiClient::Utils.deep_symbolize(hash)
end
end

Expand Down
25 changes: 12 additions & 13 deletions lib/wp_api_client/connection.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
require "faraday"
require "faraday-http-cache"
# require 'typhoeus'
# require 'typhoeus/adapters/faraday'
require "faraday/follow_redirects"

module WpApiClient
class Connection
Expand All @@ -11,33 +10,33 @@ class Connection
def initialize(configuration)
@configuration = configuration
@queue = []
@conn = Faraday.new(url: configuration.endpoint) do |faraday|
@conn = Faraday.new(url: configuration.endpoint) do |f|
# Disabled OAuth for now since Faraday Middleware is deprecated
# if configuration.oauth_credentials
# faraday.use FaradayMiddleware::OAuth, configuration.oauth_credentials
# f.use FaradayMiddleware::OAuth, configuration.oauth_credentials
# end

if configuration.basic_auth
faraday.request :authorization, :basic, configuration.basic_auth[:username], configuration.basic_auth[:password]
f.request :authorization, :basic, configuration.basic_auth[:username], configuration.basic_auth[:password]
end

if configuration.debug
faraday.response :logger
faraday.use :instrumentation
f.response :logger
f.use :instrumentation
end

if configuration.cache
faraday.use :http_cache, store: configuration.cache, shared_cache: false
f.use :http_cache, store: configuration.cache, shared_cache: false
end

if configuration.proxy
faraday.proxy configuration.proxy
f.proxy configuration.proxy
end

faraday.use Faraday::Response::RaiseError
faraday.response :json, content_type: /\bjson$/

# faraday.adapter :typhoeus
f.use Faraday::Response::RaiseError
f.response :raise_error
f.response :json, content_type: /\bjson$/
f.response :follow_redirects, limit: 0
end
end

Expand Down
6 changes: 5 additions & 1 deletion lib/wp_api_client/entities/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class Base
attr_reader :resource, :client_instance

def self.build(resource, client_instance)
raise Exception if resource.nil?
raise StandardError.new("Resource is nil") if resource.nil?
type = WpApiClient::Entities::Types.find { |type| type.represents?(resource) }
type.new(resource, client_instance)
end
Expand Down Expand Up @@ -37,6 +37,10 @@ def relations(relation, relation_to_return = nil)
def method_missing(method, *)
@resource.send(method, *)
end

def respond_to_missing?(method, include_private = false)
@resource.respond_to?(method, include_private) || super
end
end
end
end
2 changes: 1 addition & 1 deletion lib/wp_api_client/entities/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class Error
def initialize(json)
raise WpApiClient::ErrorResponse.new(json)
end

def self.represents?(json)
json.key?("code") and json.key?("message")
end
Expand Down
5 changes: 2 additions & 3 deletions lib/wp_api_client/entities/user.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
module WpApiClient
module Entities
class User < Base
alias :user :resource
alias_method :user, :resource

def self.represents?(json)
json.dig('_links', 'collection') and json['_links']['collection'].first['href'] =~ /wp\/v2\/users/
json.dig("_links", "collection") and json["_links"]["collection"].first["href"] =~ /wp\/v2\/users/
end

end
end
end
14 changes: 14 additions & 0 deletions lib/wp_api_client/utils.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module WpApiClient
module Utils
def self.deep_symbolize(obj)
case obj
when Hash
obj.each_with_object({}) { |(k, v), memo| memo[k.to_sym] = deep_symbolize(v) }
when Array
obj.map { |v| deep_symbolize(v) }
else
obj
end
end
end
end
58 changes: 50 additions & 8 deletions spec/connection_spec.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,55 @@
RSpec.describe WpApiClient::Collection do
describe "fetching posts concurrently", vcr: {cassette_name: 'concurrency', record: :new_episodes} do
RSpec.describe WpApiClient::Connection do
# describe "fetching posts concurrently", vcr: {cassette_name: "concurrency", record: :new_episodes} do
# it "allows simultaneous fetching of posts" do
# pending "parallel faraday adapter to replace typhoeus"
# resp = @api.concurrently do |api|
# api.get("posts/1")
# api.get("posts", page: 2)
# end
# expect(resp.first.title).to eq "Hello world!"
# expect(resp.last.first.title).to eq "Post 90"
# end
# end

it "allows simultaneous fetching of posts" do
resp = @api.concurrently do |api|
api.get("posts/1")
api.get("posts", page: 2)
describe "handling HTTP errors", vcr: false do
let(:configuration) do
config = WpApiClient::Configuration.new
config.endpoint = "https://example.com/wp-json/wp/v2"
config
end
let(:connection) { described_class.new(configuration) }

context "when following redirects" do
before do
stub_request(:get, "https://example.com/wp-json/wp/v2/posts")
.with(query: {"_embed" => "true"})
.to_return(
status: 301,
headers: {"Location" => "https://new-example.com/wp-json/wp/v2/posts?_embed=true"}
)
end

it "raises an error" do
expect {
connection.get("posts")
}.to raise_error(Faraday::FollowRedirects::RedirectLimitReached)
end
end

context "when receiving other connection errors" do
before do
stub_request(:get, "https://example.com/wp-json/wp/v2/posts")
.with(query: {"_embed" => "true"})
.to_return(status: 502)
end

it "raises a connection error with status information" do
expect {
connection.get("posts")
}.to raise_error(Faraday::ServerError) do |error|
expect(error.response[:status]).to eq(502)
end
end
expect(resp.first.title).to eq 'Hello world!'
expect(resp.last.first.title).to eq 'Post 90'
end
end
end
10 changes: 4 additions & 6 deletions spec/error_spec.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
RSpec.describe WpApiClient::Entities::Error do
describe "an API access error", vcr: {cassette_name: 'single_post'} do

Error_JSON = {"code"=>"rest_forbidden", "message"=>"You don't have permission to do this.", "data"=>{"status"=>403}}
let(:error_json) { {"code" => "rest_forbidden", "message" => "You don't have permission to do this.", "data" => {"status" => 403}} }

describe "an API access error", vcr: {cassette_name: "single_post"} do
it "throws an exception" do
expect {
WpApiClient::Entities::Error.new(Error_JSON)
WpApiClient::Entities::Error.new(error_json)
}.to raise_error(WpApiClient::ErrorResponse)
end

it "recognises the error JSON exception" do
expect(WpApiClient::Entities::Error.represents?(Error_JSON)).to be_truthy
expect(WpApiClient::Entities::Error.represents?(error_json)).to be_truthy
end

end
end
29 changes: 14 additions & 15 deletions wp_api_client.gemspec
Original file line number Diff line number Diff line change
@@ -1,38 +1,37 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'wp_api_client/version'
require "wp_api_client/version"

Gem::Specification.new do |spec|
spec.name = "wp-api-client"
spec.version = WpApiClient::VERSION
spec.authors = ["Duncan Brown"]
spec.email = ["[email protected]"]
spec.name = "wp-api-client"
spec.version = WpApiClient::VERSION
spec.authors = ["Duncan Brown"]
spec.email = ["[email protected]"]

spec.summary = %q{A read-only client for the WordPress REST API (v2).}
spec.homepage = "https://github.com/duncanjbrown/wp-api-client"
spec.license = "MIT"
spec.summary = "A read-only client for the WordPress REST API (v2)."
spec.homepage = "https://github.com/duncanjbrown/wp-api-client"
spec.license = "MIT"

# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
# delete this section to allow pushing this gem to any host.
if spec.respond_to?(:metadata)
spec.metadata['allowed_push_host'] = "https://rubygems.org"
spec.metadata["allowed_push_host"] = "https://rubygems.org"
else
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
end

spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]

spec.add_dependency "faraday", "~> 2.7"
spec.add_dependency "faraday-http-cache", "~> 2.5"
spec.add_dependency "faraday-follow_redirects", "~> 0.3.0"

spec.add_development_dependency "rake"
spec.add_development_dependency "rspec"
spec.add_development_dependency "vcr"
spec.add_development_dependency "webmock"
spec.add_development_dependency "debug"

end

0 comments on commit 53e4ddd

Please sign in to comment.