Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add better reply for unknown action and subscriptions (alternative implementation) #28

Merged
merged 1 commit into from Nov 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .rubocop.yml
Expand Up @@ -32,3 +32,6 @@ FactoryBot/StaticAttributeDefinedDynamically:

RSpec/ExpectChange:
EnforcedStyle: block

Style/RescueStandardError:
EnforcedStyle: implicit
4 changes: 1 addition & 3 deletions app/controllers/logux_controller.rb
Expand Up @@ -3,7 +3,6 @@
class LoguxController < ActionController::Base
include ActionController::Live

# rubocop:disable Style/RescueStandardError
def create
Logux.verify_request_meta_data(meta_params)
logux_stream.write('[')
Expand All @@ -13,13 +12,12 @@ def create
Logux.configuration.on_error.call(e)
Logux.logger.error("#{e}\n#{e.backtrace.join("\n")}")
ensure
logux_stream.write([:error].to_json)
logux_stream.write(Logux::ErrorRenderer.new(e).message)
end
ensure
logux_stream.write(']')
logux_stream.close
end
# rubocop:enable Style/RescueStandardError

private

Expand Down
19 changes: 15 additions & 4 deletions lib/logux.rb
Expand Up @@ -13,8 +13,18 @@ module Logux
extend ActiveSupport::Autoload
include Configurations

class NoPolicyError < StandardError; end
class NoActionError < StandardError; end
class WithMetaError < StandardError
attr_reader :meta

def initialize(msg, meta: nil)
@meta = meta
super(msg)
end
end

UnknownActionError = Class.new(WithMetaError)
UnknownChannelError = Class.new(WithMetaError)
UnauthorizedError = Class.new(StandardError)

autoload :Client, 'logux/client'
autoload :Meta, 'logux/meta'
Expand All @@ -35,6 +45,7 @@ class NoActionError < StandardError; end
autoload :Logger, 'logux/logger'
autoload :Version, 'logux/version'
autoload :Test, 'logux/test'
autoload :ErrorRenderer, 'logux/error_renderer'

configurable :logux_host, :verify_authorized,
:password, :logger,
Expand Down Expand Up @@ -64,13 +75,13 @@ def self.undo(meta: Logux::Meta.new({}), reason: nil)

def self.verify_request_meta_data(meta_params)
if Logux.configuration.password.nil?
logger.warn(%(Please, add passoword for logux server:
logger.warn(%(Please, add password for logux server:
Logux.configure do |c|
c.password = 'your-password'
end))
end
auth = Logux.configuration.password == meta_params&.dig(:password)
raise unless auth
raise Logux::UnauthorizedError, 'Incorrect password' unless auth
end

def self.process_batch(stream:, batch:)
Expand Down
5 changes: 4 additions & 1 deletion lib/logux/action_caller.rb
Expand Up @@ -15,6 +15,9 @@ def call!
action_class = class_finder.find_action_class
@action_controller = action_class.new(action: action, meta: meta)
format(action_controller.public_send(action.action_type))
rescue Logux::UnknownActionError, Logux::UnknownChannelError => e
Logux.logger.warn(e)
format(nil)
end

private
Expand All @@ -26,7 +29,7 @@ def format(response)
end

def class_finder
@class_finder ||= Logux::ClassFinder.new(action)
@class_finder ||= Logux::ClassFinder.new(action: action, meta: meta)
end
end
end
45 changes: 30 additions & 15 deletions lib/logux/class_finder.rb
Expand Up @@ -2,41 +2,56 @@

module Logux
class ClassFinder
attr_reader :params
attr_reader :action, :meta

def initialize(params)
@params = params
def initialize(action:, meta:)
@action = action
@meta = meta
end

def find_action_class
"#{class_namespace}::#{class_name}".constantize
rescue NameError
raise Logux::NoActionError, %(
message = %(
Unable to find action #{class_name.camelize}
Should be in app/logux/actions/#{class_path}.rb
Should be in app/logux/#{class_namespace.downcase}/#{class_path}.rb
)
raise Logux::UnknownActionError.new(message, meta: meta) if action?
raise Logux::UnknownChannelError.new(message, meta: meta)
end

def find_policy_class
"Policies::#{class_namespace}::#{class_name}".constantize
rescue NameError
raise Logux::NoPolicyError, %(
Unable to find policy #{class_name.camelize}
Should be in app/logux/policies/#{class_path}.rb
message = %(
Unable to find action policy #{class_name.camelize}
Should be in app/logux/policies/#{class_namespace.downcase}/#{class_path}.rb
)
raise Logux::UnknownActionError.new(message, meta: meta) if action?
raise Logux::UnknownChannelError.new(message, meta: meta)
end

def class_name
if subscribe?
action.channel_name.camelize
else
action.type.split('/')[0..-2].map(&:camelize).join('::')
end
end

private

def class_namespace
return 'Channels' if params.type == 'logux/subscribe'
return 'Channels' if subscribe?
'Actions'
end

def class_name
if params.type == 'logux/subscribe'
params.channel_name.camelize
else
params.type.split('/')[0..-2].map(&:camelize).join('::')
end
def subscribe?
action.type == 'logux/subscribe'
end

def action?
!subscribe?
end

def class_path
Expand Down
32 changes: 32 additions & 0 deletions lib/logux/error_renderer.rb
@@ -0,0 +1,32 @@
# frozen_string_literal: true

module Logux
class ErrorRenderer
attr_reader :exception

def initialize(exception)
@exception = exception
end

def message
case exception
when Logux::WithMetaError
build_message(exception, exception.meta.id)
when Logux::UnauthorizedError
build_message(exception, exception.message)
when StandardError
# some runtime error that should be fixed
['error', 'Please look server logs for more information']
end
end

private

def build_message(exception, additional_info)
[
exception.class.name.demodulize.camelize(:lower).gsub(/Error/, ''),
additional_info
]
end
end
end
4 changes: 2 additions & 2 deletions lib/logux/policy_caller.rb
Expand Up @@ -15,13 +15,13 @@ def call!
policy_class = class_finder.find_policy_class
@policy = policy_class.new(action: action, meta: meta)
policy.public_send("#{action.action_type}?")
rescue Logux::NoPolicyError => e
rescue Logux::UnknownActionError, Logux::UnknownChannelError => e
raise e if Logux.configuration.verify_authorized
Logux.logger.warn(e)
end

def class_finder
@class_finder ||= Logux::ClassFinder.new(action)
@class_finder ||= Logux::ClassFinder.new(action: action, meta: meta)
end
end
end
11 changes: 11 additions & 0 deletions spec/dummy/app/logux/policies/actions/policy_without_action.rb
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Policies
module Actions
class PolicyWithoutAction < Logux::Policy
def create?
true
end
end
end
end
11 changes: 11 additions & 0 deletions spec/dummy/app/logux/policies/channels/policy_without_channel.rb
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module Policies
module Channels
class PolicyWithoutChannel < Logux::Policy
def subscribe?
true
end
end
end
end
9 changes: 9 additions & 0 deletions spec/factories/logux_actions_factory.rb
Expand Up @@ -18,5 +18,14 @@
key { 'name' }
value { 'test1' }
end

factory :logux_actions_unknown do
type 'unknown/action'
end

factory :logux_actions_unknown_subscribe do
type 'logux/subscribe'
channel 'unknown/channel'
end
end
end
4 changes: 0 additions & 4 deletions spec/logux/action_caller_spec.rb
Expand Up @@ -8,10 +8,6 @@
let(:meta) { create(:logux_meta) }

describe '#call!' do
it 'raise error' do
expect { action_caller.call! }.to raise_error(Logux::NoActionError)
end

context 'when action defined' do
subject(:result) { action_caller.call! }

Expand Down
41 changes: 36 additions & 5 deletions spec/logux/class_finder_spec.rb
Expand Up @@ -3,12 +3,13 @@
require 'spec_helper'

describe Logux::ClassFinder do
let(:finder) { described_class.new(params) }
let(:finder) { described_class.new(action: action, meta: meta) }
let(:meta) { create(:logux_meta) }

describe '#class_name' do
subject(:class_name) { finder.class_name }

let(:params) { create(:logux_actions_add, type: 'test/test/name/try/user/add') }
let(:action) { create(:logux_actions_add, type: 'test/test/name/try/user/add') }

it 'returns nested classes' do
expect(class_name).to eq('Test::Test::Name::Try::User')
Expand All @@ -18,10 +19,40 @@
describe '#find_policy_class' do
subject(:policy_class) { finder.find_policy_class }

let(:params) { create(:logux_actions_add, type: 'test/test/name/try/user/add') }
context 'with unknown action' do
let(:action) { create(:logux_actions_unknown) }

it 'raise an error' do
expect { policy_class }.to raise_error(Logux::NoPolicyError)
it 'raise an error for unknown action error' do
expect { policy_class }.to raise_error(Logux::UnknownActionError)
end
end

context 'with unknown subscribe' do
let(:action) { create(:logux_actions_unknown_subscribe) }

it 'raise an error for unknown action error' do
expect { policy_class }.to raise_error(Logux::UnknownChannelError)
end
end
end

describe '#find_action_class' do
subject(:action_class) { finder.find_action_class }

context 'with unknown action' do
let(:action) { create(:logux_actions_unknown) }

it 'raise an error for unknown action error' do
expect { action_class }.to raise_error(Logux::UnknownActionError)
end
end

context 'with unknown subscribe' do
let(:action) { create(:logux_actions_unknown_subscribe) }

it 'raise an error for unknown action error' do
expect { action_class }.to raise_error(Logux::UnknownChannelError)
end
end
end
end
39 changes: 39 additions & 0 deletions spec/logux/error_renderer_spec.rb
@@ -0,0 +1,39 @@
# frozen_string_literal: true

require 'spec_helper'

describe Logux::ErrorRenderer do
let(:meta) { create(:logux_meta, id: 123) }

describe '#message' do
def build_message(exception)
described_class.new(exception).message
end

it 'returns correct error message for UnknownActionError' do
exception = Logux::UnknownActionError.new('test', meta: meta)

expect(build_message(exception)).to eq(['unknownAction', 123])
end

it 'returns correct error message for UnknownChannelError' do
exception = Logux::UnknownChannelError.new('test', meta: meta)

expect(build_message(exception)).to eq(['unknownChannel', 123])
end

it 'returns correct error message for UnauthorizedError' do
exception = Logux::UnauthorizedError.new('test')

expect(build_message(exception)).to eq(%w[unauthorized test])
end

it 'returns correct error message for some unknown error' do
exception = StandardError.new

expect(build_message(exception)).to eq(
['error', 'Please look server logs for more information']
)
end
end
end