diff --git a/lib/easee/client.rb b/lib/easee/client.rb index 4b9a512..1179200 100644 --- a/lib/easee/client.rb +++ b/lib/easee/client.rb @@ -189,11 +189,21 @@ def raise_credentials_or_request_error(error) raise Errors::InvalidPinCode.new("PIN code rejected", error.response) when Errors::ChargerNotFound::CODE raise Errors::ChargerNotFound.new("Charger not found", error.response) + when Errors::PAIRING_ERROR_CODE + raise_pairing_result_error(error) end raise request_failed(error) end + def raise_pairing_result_error(error) + title = error.response&.dig(:body, "title") + error_class = Errors::PAIRING_RESULT_ERRORS.find { |klass| klass::PAIRING_RESULT_TITLES.include?(title) } + return unless error_class + + raise error_class.new("Pairing rejected: #{title}", error.response) + end + def request_failed(error) Errors::RequestFailed.new("Request returned status #{error.response_status}", error.response) end diff --git a/lib/easee/errors.rb b/lib/easee/errors.rb index e1c6eaa..71001e2 100644 --- a/lib/easee/errors.rb +++ b/lib/easee/errors.rb @@ -21,14 +21,33 @@ class Forbidden < Base; end class InvalidPinCode < RequestFailed CODE = 193 + PAIRING_RESULT_TITLES = %w[IncorrectPIN].freeze end class ChargerNotFound < RequestFailed CODE = 400 end + class TooManyAttempts < RequestFailed + PAIRING_RESULT_TITLES = %w[TooManyAttempts].freeze + end + + class AlreadyPaired < RequestFailed + PAIRING_RESULT_TITLES = %w[AlreadyPairedWithPartner AlreadyPairedWithUser].freeze + end + class RateLimitExceeded < RequestFailed def retryable? = true end + + # https://developer.easee.com/docs/enumerations — pairing result codes (0..7) + # ride on a single HTTP errorCode of 123, with the specific reason in `title`. + PAIRING_ERROR_CODE = 123 + + PAIRING_RESULT_ERRORS = [ + InvalidPinCode, + TooManyAttempts, + AlreadyPaired, + ].freeze end end diff --git a/spec/easee/client_spec.rb b/spec/easee/client_spec.rb index 56fada8..c82e4d5 100644 --- a/spec/easee/client_spec.rb +++ b/spec/easee/client_spec.rb @@ -367,6 +367,131 @@ expect { client.pair(charger_id: "UNKNOWN", pin_code: "1234") } .to raise_error(Easee::Errors::ChargerNotFound) end + + it "raises InvalidPinCode on a PairingError with title IncorrectPIN" do + token_cache = ActiveSupport::Cache::MemoryStore.new + token_cache.write( + Easee::Client::TOKENS_CACHE_KEY, + { "accessToken" => "T123" }.to_json, + ) + + stub_request(:post, "https://api.easee.cloud/api/chargers/123ABC/pair?pinCode=WRONG") + .with(headers: { "Authorization" => "Bearer T123" }) + .to_return( + status: 400, + body: { + errorCode: 123, + errorCodeName: "PairingError", + title: "IncorrectPIN", + }.to_json, + headers: { "Content-Type": "application/json" }, + ) + + client = Easee::Client.new(user_name: "easee", password: "money", token_cache:) + + expect { client.pair(charger_id: "123ABC", pin_code: "WRONG") } + .to raise_error(Easee::Errors::InvalidPinCode) + end + + it "raises TooManyAttempts on a PairingError with title TooManyAttempts" do + token_cache = ActiveSupport::Cache::MemoryStore.new + token_cache.write( + Easee::Client::TOKENS_CACHE_KEY, + { "accessToken" => "T123" }.to_json, + ) + + stub_request(:post, "https://api.easee.cloud/api/chargers/123ABC/pair?pinCode=1234") + .with(headers: { "Authorization" => "Bearer T123" }) + .to_return( + status: 400, + body: { + errorCode: 123, + errorCodeName: "PairingError", + title: "TooManyAttempts", + }.to_json, + headers: { "Content-Type": "application/json" }, + ) + + client = Easee::Client.new(user_name: "easee", password: "money", token_cache:) + + expect { client.pair(charger_id: "123ABC", pin_code: "1234") } + .to raise_error(Easee::Errors::TooManyAttempts) + end + + it "raises AlreadyPaired on a PairingError with title AlreadyPairedWithPartner" do + token_cache = ActiveSupport::Cache::MemoryStore.new + token_cache.write( + Easee::Client::TOKENS_CACHE_KEY, + { "accessToken" => "T123" }.to_json, + ) + + stub_request(:post, "https://api.easee.cloud/api/chargers/123ABC/pair?pinCode=1234") + .with(headers: { "Authorization" => "Bearer T123" }) + .to_return( + status: 400, + body: { + errorCode: 123, + errorCodeName: "PairingError", + title: "AlreadyPairedWithPartner", + }.to_json, + headers: { "Content-Type": "application/json" }, + ) + + client = Easee::Client.new(user_name: "easee", password: "money", token_cache:) + + expect { client.pair(charger_id: "123ABC", pin_code: "1234") } + .to raise_error(Easee::Errors::AlreadyPaired) + end + + it "raises AlreadyPaired on a PairingError with title AlreadyPairedWithUser" do + token_cache = ActiveSupport::Cache::MemoryStore.new + token_cache.write( + Easee::Client::TOKENS_CACHE_KEY, + { "accessToken" => "T123" }.to_json, + ) + + stub_request(:post, "https://api.easee.cloud/api/chargers/123ABC/pair?pinCode=1234") + .with(headers: { "Authorization" => "Bearer T123" }) + .to_return( + status: 400, + body: { + errorCode: 123, + errorCodeName: "PairingError", + title: "AlreadyPairedWithUser", + }.to_json, + headers: { "Content-Type": "application/json" }, + ) + + client = Easee::Client.new(user_name: "easee", password: "money", token_cache:) + + expect { client.pair(charger_id: "123ABC", pin_code: "1234") } + .to raise_error(Easee::Errors::AlreadyPaired) + end + + it "raises the generic RequestFailed on a PairingError with an unknown title" do + token_cache = ActiveSupport::Cache::MemoryStore.new + token_cache.write( + Easee::Client::TOKENS_CACHE_KEY, + { "accessToken" => "T123" }.to_json, + ) + + stub_request(:post, "https://api.easee.cloud/api/chargers/123ABC/pair?pinCode=1234") + .with(headers: { "Authorization" => "Bearer T123" }) + .to_return( + status: 400, + body: { + errorCode: 123, + errorCodeName: "PairingError", + title: "SomethingElse", + }.to_json, + headers: { "Content-Type": "application/json" }, + ) + + client = Easee::Client.new(user_name: "easee", password: "money", token_cache:) + + expect { client.pair(charger_id: "123ABC", pin_code: "1234") } + .to raise_error(Easee::Errors::RequestFailed) + end end describe "#unpair" do