Skip to content

Commit

Permalink
Merge pull request #12 from stekker/forbidden
Browse files Browse the repository at this point in the history
Introduce Forbidden and InvalidCredentials errors
  • Loading branch information
bforma authored Jan 31, 2024
2 parents ad0a9d5 + 480e7f0 commit ceaf8c7
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 7 deletions.
18 changes: 11 additions & 7 deletions lib/easee/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,13 @@ def connection
Faraday.new(url: BASE_URL) do |conn|
conn.request :json
conn.response :raise_error
conn.response :json, content_type: /\bjson$/
end
end

def authenticated_connection
connection.tap do |conn|
conn.request :authorization, "Bearer", access_token
conn.response :json, content_type: /\bjson$/
end
end

Expand Down Expand Up @@ -116,17 +116,21 @@ def with_error_handling

retry
end
rescue Faraday::TooManyRequestsError => e
raise Errors::RateLimitExceeded.new("Rate limit exceeded", e.response)
rescue Faraday::ForbiddenError => e
raise Errors::Forbidden, "Access denied to charger"
rescue Faraday::Error => e
if e.response_status == 429 # HTTP 429 TooManyRequests
raise Errors::RateLimitExceeded.new("Rate limit exceeded", e.response)
else
raise Errors::RequestFailed.new("Request returned status #{e.response_status}", e.response)
if e.response_status == 400 && e.response.dig(:body, "errorCode") == 100
raise Errors::InvalidCredentials, "Invalid username or password"
end

raise Errors::RequestFailed.new("Request returned status #{e.response_status}", e.response)
end

def access_token
encrypted_tokens = @token_cache.fetch(TOKENS_CACHE_KEY) do
@encryptor.encrypt(request_access_token, cipher_options: { deterministic: true })
@encryptor.encrypt(request_access_token.to_json, cipher_options: { deterministic: true })
end

plain_text_tokens = @encryptor.decrypt(encrypted_tokens)
Expand All @@ -137,7 +141,7 @@ def access_token
def refresh_access_token!
@token_cache.write(
TOKENS_CACHE_KEY,
@encryptor.encrypt(refresh_access_token, cipher_options: { deterministic: true }),
@encryptor.encrypt(refresh_access_token.to_json, cipher_options: { deterministic: true }),
expires_in: 1.day,
)
rescue Faraday::Error => e
Expand Down
4 changes: 4 additions & 0 deletions lib/easee/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ class Base < ::StandardError
def retryable? = false
end

class InvalidCredentials < Base; end

class RequestFailed < Base
attr_reader :response

Expand All @@ -13,6 +15,8 @@ def initialize(message, response = nil)
end
end

class Forbidden < Base; end

class RateLimitExceeded < RequestFailed
def retryable? = true
end
Expand Down
44 changes: 44 additions & 0 deletions spec/easee/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,35 @@
expect(encryptor).to have_received(:encrypt).with(tokens.to_json, cipher_options: { deterministic: true })
expect(encryptor).to have_received(:decrypt).with("encrypted")
end

it "raises when the access token could not be obtained" do
user_name = "test"
password = "wrong"

stub_request(:post, "https://api.easee.cloud/api/accounts/login")
.with(
body: { userName: user_name, password: }.to_json,
)
.to_return(
status: 400,
body: {
errorCode: 100,
errorCodeName: "InvalidUserPassword",
type: nil,
title: "Username or password is invalid",
status: 400,
detail: "[Empty in production]",
instance: nil,
extensions: {},
}.to_json,
headers: { "Content-Type": "application/json" },
)

client = Easee::Client.new(user_name:, password:)

expect { client.pair(charger_id: "123ABC", pin_code: "1234") }
.to raise_error(Easee::Errors::InvalidCredentials)
end
end

describe "#pair" do
Expand Down Expand Up @@ -374,6 +403,21 @@
expect(error).to be_retryable
end
end

it "raises a Forbidden error when we have no access to the charger (anymore)" do
stub_request(:get, "https://api.easee.cloud/api/chargers/C123/state")
.to_return(status: 403)

token_cache = ActiveSupport::Cache::MemoryStore.new
token_cache.write(
Easee::Client::TOKENS_CACHE_KEY,
{ "accessToken" => "T123" }.to_json,
)

client = Easee::Client.new(user_name: "easee", password: "money", token_cache:)

expect { client.state("C123") }.to raise_error(Easee::Errors::Forbidden)
end
end

describe "#pause_charging" do
Expand Down

0 comments on commit ceaf8c7

Please sign in to comment.