From f606eb040e87432b9ae69683bdf56f051175513c Mon Sep 17 00:00:00 2001 From: Sunny Ripert Date: Tue, 3 Jan 2023 13:14:07 +0100 Subject: [PATCH 1/2] Add custom exception for error responses --- lib/thingiverse.rb | 1 + lib/thingiverse/connection.rb | 4 ++-- lib/thingiverse/pagination.rb | 17 ++++++++--------- lib/thingiverse/response_error.rb | 19 +++++++++++++++++++ lib/thingiverse/tags.rb | 4 ++-- lib/thingiverse/things.rb | 22 +++++++++++----------- lib/thingiverse/users.rb | 4 ++-- 7 files changed, 45 insertions(+), 26 deletions(-) create mode 100644 lib/thingiverse/response_error.rb diff --git a/lib/thingiverse.rb b/lib/thingiverse.rb index 672fb06..0733a8c 100644 --- a/lib/thingiverse.rb +++ b/lib/thingiverse.rb @@ -8,6 +8,7 @@ require 'thingiverse/version' require 'thingiverse/dynamic_attributes' require 'thingiverse/connection' +require 'thingiverse/response_error' require 'thingiverse/pagination' require 'thingiverse/things' require 'thingiverse/files' diff --git a/lib/thingiverse/connection.rb b/lib/thingiverse/connection.rb index 89bfc79..da50e8d 100644 --- a/lib/thingiverse/connection.rb +++ b/lib/thingiverse/connection.rb @@ -35,7 +35,7 @@ def access_token=(token) def get_token auth_response = self.class.post(auth_url, :query => {:client_id => @client_id, :client_secret => @client_secret, :code => @code}) - raise "#{auth_response.code}: #{auth_response.body.inspect}" unless auth_response.success? + raise ResponseError.new(auth_response) unless auth_response.success? response = CGI::parse(auth_response.parsed_response) @@ -53,7 +53,7 @@ def things def users Thingiverse::Users end - + def tags Thingiverse::Tags end diff --git a/lib/thingiverse/pagination.rb b/lib/thingiverse/pagination.rb index ab33ad5..7f7aa17 100644 --- a/lib/thingiverse/pagination.rb +++ b/lib/thingiverse/pagination.rb @@ -4,17 +4,16 @@ module Thingiverse class Pagination include Enumerable extend Forwardable - + attr_reader :response, :object attr_reader :current_page, :total_pages, :first_url, :last_url, :next_url, :prev_url - + def initialize(response, object) @response = response @object = object - # TODO: provide more debug info and raise a custom exception - raise "#{@response.code}: #{JSON.parse(@response.body)['error']}" unless @response.success? - + raise ResponseError.new(@response) unless @response.success? + @objects = @response.parsed_response.collect do |attrs| @object.new attrs end @@ -38,7 +37,7 @@ def initialize(response, object) end def_delegators :@objects, :<<, :[], :[]=, :last, :size - + def method_missing(meth, *args, &block) if meth.to_s =~ /^(.*)_page$/ get_url_page($1, *args, &block) @@ -46,14 +45,14 @@ def method_missing(meth, *args, &block) super end end - + def get_url_page(which, *args, &block) url = instance_variable_get("@#{which}_url") Thingiverse::Pagination.new(Thingiverse::Connection.get(url), @object) if url end - + def each(&block) @objects.each(&block) - end + end end end diff --git a/lib/thingiverse/response_error.rb b/lib/thingiverse/response_error.rb new file mode 100644 index 0000000..2df9a86 --- /dev/null +++ b/lib/thingiverse/response_error.rb @@ -0,0 +1,19 @@ +module Thingiverse + class ResponseError < StandardError + def initialize(response) + @response = response + end + + attr_reader :response + + def message + "#{response.code}: #{message_body} #{response.headers['x-error']}".strip + end + + def message_body + JSON.parse(response.body)['error'] + rescue + response.body + end + end +end diff --git a/lib/thingiverse/tags.rb b/lib/thingiverse/tags.rb index 91bc852..70eec53 100644 --- a/lib/thingiverse/tags.rb +++ b/lib/thingiverse/tags.rb @@ -4,10 +4,10 @@ class Tags def self.find(tag_name) response = Thingiverse::Connection.get("/tags/#{tag_name}") - raise "#{response.code}: #{JSON.parse(response.body)['error']}" unless response.success? + raise ResponseError.new(response) unless response.success? self.new response.parsed_response end - + def things(query = {}) Thingiverse::Pagination.new(Thingiverse::Connection.get(things_url, :query => query), Thingiverse::Things) end diff --git a/lib/thingiverse/things.rb b/lib/thingiverse/things.rb index ba316e7..d4b6a6c 100644 --- a/lib/thingiverse/things.rb +++ b/lib/thingiverse/things.rb @@ -4,7 +4,7 @@ class Things def user response = Thingiverse::Connection.get("/users/#{creator['name']}") - raise "#{response.code}: #{JSON.parse(response.body)['error']}" unless response.success? + raise ResponseError.new(response) unless response.success? Thingiverse::Users.new response.parsed_response end @@ -26,7 +26,7 @@ def ancestors(query = {}) def tags response = Thingiverse::Connection.get(tags_url) - raise "#{response.code}: #{JSON.parse(response.body)['error']}" unless response.success? + raise ResponseError.new(response) unless response.success? response.parsed_response.collect do |attrs| Thingiverse::Tags.new attrs end @@ -37,7 +37,7 @@ def save thing = Thingiverse::Things.create(@attributes) else response = Thingiverse::Connection.patch("/things/#{id}", :body => @attributes.to_json) - raise "#{response.code}: #{JSON.parse(response.body)['error']}" unless response.success? + raise ResponseError.new(response) unless response.success? thing = Thingiverse::Things.new(response.parsed_response) end @@ -46,7 +46,7 @@ def save send("#{name}=", value) end end - + # file_or_string can be a File or a String. # thingiverse_filename is optional if using a File (the File filename will be used by default) but is required if using a String def upload(file_or_string, thingiverse_filename=nil) @@ -59,11 +59,11 @@ def upload(file_or_string, thingiverse_filename=nil) else raise ArgumentError, "file_or_string not of accepted type. Expected File or String. Actual: #{file_or_string.class}" end - + raise ArgumentError, "Unable to determine filename" if thingiverse_filename.to_s == "" - + response = Thingiverse::Connection.post("/things/#{id}/files", :body => {:filename => thingiverse_filename}.to_json) - raise "#{response.code}: #{JSON.parse(response.body)['error']} #{response.headers['x-error']}" unless response.success? + raise ResponseError.new(response) unless response.success? parsed_response = JSON.parse(response.body) action = parsed_response["action"] @@ -96,7 +96,7 @@ def upload(file_or_string, thingiverse_filename=nil) if c.response_code == 200 # finalize it response = Thingiverse::Connection.post(query['success_action_redirect']) - raise "#{response.code}: #{JSON.parse(response.body)['error']} #{response.headers['x-error']}" unless response.success? + raise ResponseError.new(response) unless response.success? Thingiverse::Files.new(response.parsed_response) else raise "#{c.response_code}: #{c.body_str}" @@ -108,7 +108,7 @@ def publish raise "Cannot publish until thing is saved" else response = Thingiverse::Connection.post("/things/#{id}/publish") - raise "#{response.code}: #{JSON.parse(response.body)['error']}" unless response.success? + raise ResponseError.new(response) unless response.success? thing = Thingiverse::Things.new(response.parsed_response) end @@ -120,7 +120,7 @@ def publish def self.find(thing_id) response = Thingiverse::Connection.get("/things/#{thing_id}") - raise "#{response.code}: #{JSON.parse(response.body)['error']} #{response.headers['x-error']}" unless response.success? + raise ResponseError.new(response) unless response.success? self.new response.parsed_response end @@ -132,7 +132,7 @@ def self.create(params) thing = self.new(params) response = Thingiverse::Connection.post('/things', :body => thing.attributes.to_json) - raise "#{response.code}: #{JSON.parse(response.body)['error']} #{response.headers['x-error']}" unless response.success? + raise ResponseError.new(response) unless response.success? self.new(response.parsed_response) end diff --git a/lib/thingiverse/users.rb b/lib/thingiverse/users.rb index 1f12bc8..4d3a3a1 100644 --- a/lib/thingiverse/users.rb +++ b/lib/thingiverse/users.rb @@ -1,10 +1,10 @@ module Thingiverse class Users include Thingiverse::DynamicAttributes - + def self.find(user_name) response = Thingiverse::Connection.get("/users/#{user_name}") - raise "#{response.code}: #{JSON.parse(response.body)['error']}" unless response.success? + raise ResponseError.new(response) unless response.success? self.new response.parsed_response end end From 27948ca0807a91553e51296156542c3152380682 Mon Sep 17 00:00:00 2001 From: Sunny Ripert Date: Tue, 20 Feb 2024 22:04:31 +0100 Subject: [PATCH 2/2] Exception when rate limit is exceeded --- lib/thingiverse/connection.rb | 2 +- lib/thingiverse/pagination.rb | 2 +- lib/thingiverse/rate_limit_exceeded_error.rb | 2 ++ lib/thingiverse/response_error.rb | 9 +++++++++ lib/thingiverse/tags.rb | 2 +- lib/thingiverse/things.rb | 16 ++++++++-------- lib/thingiverse/users.rb | 2 +- 7 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 lib/thingiverse/rate_limit_exceeded_error.rb diff --git a/lib/thingiverse/connection.rb b/lib/thingiverse/connection.rb index da50e8d..e0c0b54 100644 --- a/lib/thingiverse/connection.rb +++ b/lib/thingiverse/connection.rb @@ -35,7 +35,7 @@ def access_token=(token) def get_token auth_response = self.class.post(auth_url, :query => {:client_id => @client_id, :client_secret => @client_secret, :code => @code}) - raise ResponseError.new(auth_response) unless auth_response.success? + raise ResponseError.from(auth_response) unless auth_response.success? response = CGI::parse(auth_response.parsed_response) diff --git a/lib/thingiverse/pagination.rb b/lib/thingiverse/pagination.rb index 7f7aa17..8bbf8a0 100644 --- a/lib/thingiverse/pagination.rb +++ b/lib/thingiverse/pagination.rb @@ -12,7 +12,7 @@ def initialize(response, object) @response = response @object = object - raise ResponseError.new(@response) unless @response.success? + raise ResponseError.from(@response) unless @response.success? @objects = @response.parsed_response.collect do |attrs| @object.new attrs diff --git a/lib/thingiverse/rate_limit_exceeded_error.rb b/lib/thingiverse/rate_limit_exceeded_error.rb new file mode 100644 index 0000000..75c7d4d --- /dev/null +++ b/lib/thingiverse/rate_limit_exceeded_error.rb @@ -0,0 +1,2 @@ +class RateLimitExceededError < ResponseError +end diff --git a/lib/thingiverse/response_error.rb b/lib/thingiverse/response_error.rb index 2df9a86..6f8f5ef 100644 --- a/lib/thingiverse/response_error.rb +++ b/lib/thingiverse/response_error.rb @@ -1,5 +1,14 @@ module Thingiverse class ResponseError < StandardError + def self.from(response) + case response.code.to_i + when 420, 429 + RateLimitExceededError.new(response) + else + new(response) + end + end + def initialize(response) @response = response end diff --git a/lib/thingiverse/tags.rb b/lib/thingiverse/tags.rb index 70eec53..95b0009 100644 --- a/lib/thingiverse/tags.rb +++ b/lib/thingiverse/tags.rb @@ -4,7 +4,7 @@ class Tags def self.find(tag_name) response = Thingiverse::Connection.get("/tags/#{tag_name}") - raise ResponseError.new(response) unless response.success? + raise ResponseError.from(response) unless response.success? self.new response.parsed_response end diff --git a/lib/thingiverse/things.rb b/lib/thingiverse/things.rb index d4b6a6c..f4abdfc 100644 --- a/lib/thingiverse/things.rb +++ b/lib/thingiverse/things.rb @@ -4,7 +4,7 @@ class Things def user response = Thingiverse::Connection.get("/users/#{creator['name']}") - raise ResponseError.new(response) unless response.success? + raise ResponseError.from(response) unless response.success? Thingiverse::Users.new response.parsed_response end @@ -26,7 +26,7 @@ def ancestors(query = {}) def tags response = Thingiverse::Connection.get(tags_url) - raise ResponseError.new(response) unless response.success? + raise ResponseError.from(response) unless response.success? response.parsed_response.collect do |attrs| Thingiverse::Tags.new attrs end @@ -37,7 +37,7 @@ def save thing = Thingiverse::Things.create(@attributes) else response = Thingiverse::Connection.patch("/things/#{id}", :body => @attributes.to_json) - raise ResponseError.new(response) unless response.success? + raise ResponseError.from(response) unless response.success? thing = Thingiverse::Things.new(response.parsed_response) end @@ -63,7 +63,7 @@ def upload(file_or_string, thingiverse_filename=nil) raise ArgumentError, "Unable to determine filename" if thingiverse_filename.to_s == "" response = Thingiverse::Connection.post("/things/#{id}/files", :body => {:filename => thingiverse_filename}.to_json) - raise ResponseError.new(response) unless response.success? + raise ResponseError.from(response) unless response.success? parsed_response = JSON.parse(response.body) action = parsed_response["action"] @@ -96,7 +96,7 @@ def upload(file_or_string, thingiverse_filename=nil) if c.response_code == 200 # finalize it response = Thingiverse::Connection.post(query['success_action_redirect']) - raise ResponseError.new(response) unless response.success? + raise ResponseError.from(response) unless response.success? Thingiverse::Files.new(response.parsed_response) else raise "#{c.response_code}: #{c.body_str}" @@ -108,7 +108,7 @@ def publish raise "Cannot publish until thing is saved" else response = Thingiverse::Connection.post("/things/#{id}/publish") - raise ResponseError.new(response) unless response.success? + raise ResponseError.from(response) unless response.success? thing = Thingiverse::Things.new(response.parsed_response) end @@ -120,7 +120,7 @@ def publish def self.find(thing_id) response = Thingiverse::Connection.get("/things/#{thing_id}") - raise ResponseError.new(response) unless response.success? + raise ResponseError.from(response) unless response.success? self.new response.parsed_response end @@ -132,7 +132,7 @@ def self.create(params) thing = self.new(params) response = Thingiverse::Connection.post('/things', :body => thing.attributes.to_json) - raise ResponseError.new(response) unless response.success? + raise ResponseError.from(response) unless response.success? self.new(response.parsed_response) end diff --git a/lib/thingiverse/users.rb b/lib/thingiverse/users.rb index 4d3a3a1..2e94196 100644 --- a/lib/thingiverse/users.rb +++ b/lib/thingiverse/users.rb @@ -4,7 +4,7 @@ class Users def self.find(user_name) response = Thingiverse::Connection.get("/users/#{user_name}") - raise ResponseError.new(response) unless response.success? + raise ResponseError.from(response) unless response.success? self.new response.parsed_response end end