Skip to content

Commit

Permalink
feat(fallbacks): do not allow fallbacks for method steps
Browse files Browse the repository at this point in the history
  • Loading branch information
marian13 committed May 28, 2024
1 parent a577fbd commit f90abdd
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 13 deletions.
14 changes: 14 additions & 0 deletions BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,17 @@ Although it is very flexible and easy to reuse from the maintenance point of vie
In case it starts to cause any visible performance penalty, it can be refactored using regular `||=` or `if defined?`.

---

### Consider to create parent result while copying results

| Priority | Complexity | Status | Tags |
| - | - | - | - |
| Medium | Moderate | TODO | parent-result |

Fallbacks have no access to their original results.

When fallback result is used, original result is NOT in the parents chain.

It is OK?

---
6 changes: 4 additions & 2 deletions lib/convenient_service/service/configs/standard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,13 @@ class self::Step
end

middlewares :result do
use ConvenientService::Plugins::Step::CanHaveFallbacks::Middleware.with(fallback_true_status: :failure)

insert_after \
ConvenientService::Plugins::Step::HasResult::Middleware,
ConvenientService::Plugins::Step::CanHaveParentResult::Middleware

insert_after \
ConvenientService::Plugins::Step::HasResult::Middleware,
ConvenientService::Plugins::Step::CanHaveFallbacks::Middleware.with(fallback_true_status: :failure)
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ module ConvenientService
module Service
module Plugins
module CanHaveRollbacks
##
# @api private
#
class Middleware < MethodChainMiddleware
intended_for :result, entity: :service

Expand All @@ -27,6 +24,9 @@ def next(...)
# @param result [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
# @return [void]
#
# @internal
# IMPORTANT: Uses recursion inside `rollback_organizer`. Recursion has two exit conditions. When service has no steps. When service has only method steps.
#
def rollback_result(result)
result.from_step? ? rollback_organizer(result.service) : rollback_service(result.service)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ module Plugins
module CanHaveFallbacks
module Exceptions
class FallbackResultIsNotOverridden < ::ConvenientService::Exception
##
# @param step [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step]
# @param service [ConvenientService::Service]
# @param status [Symbol]
# @return [void]
#
def initialize_with_kwargs(step:, service:, status:)
message = <<~TEXT
Neither `fallback_#{status}_result` nor `fallback_result` methods of `#{service.class}` are overridden, but the step is marked to be fallbacked.
Expand All @@ -20,6 +26,22 @@ def initialize_with_kwargs(step:, service:, status:)
initialize(message)
end
end

class MethodStepCanNotHaveFallback < ::ConvenientService::Exception
##
# @param step [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step]
# @return [void]
#
def initialize_with_kwargs(step:)
message = <<~TEXT
Method step can NOT have fallback.
Either remove `fallback` option from step `:#{step.method}` in `#{step.container.klass}` or consider to refactor it into a service step.
TEXT

initialize(message)
end
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,21 @@ def fallback_true_status

##
# @return [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
# @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::FallbackResultIsNotOverridden]
# @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::FallbackResultIsNotOverridden, ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::MethodStepCanNotHaveFallback]
#
def fallback_failure_result
refute_method_step!

fallback_failure_result_own_method&.call || fallback_result_own_method&.call || ::ConvenientService.raise(Exceptions::FallbackResultIsNotOverridden.new(step: step, service: service, status: :failure))
end

##
# @return [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
# @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::FallbackResultIsNotOverridden]
# @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::FallbackResultIsNotOverridden, ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::MethodStepCanNotHaveFallback]
#
def fallback_error_result
refute_method_step!

fallback_error_result_own_method&.call || fallback_result_own_method&.call || ::ConvenientService.raise(Exceptions::FallbackResultIsNotOverridden.new(step: step, service: service, status: :error))
end

Expand Down Expand Up @@ -115,6 +119,19 @@ def fallback_result_own_method
def service
@service ||= step.service.klass.new(**step.input_values)
end

##
# @return [void]
# @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions::MethodStepCanNotHaveFallback]
#
# @internal
# TODO: Consider to move this assertion to the build time.
#
def refute_method_step!
return unless step.method_step?

raise Exceptions::MethodStepCanNotHaveFallback.new(step: step)
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,10 +532,10 @@
ConvenientService::Common::Plugins::CachesReturnValue::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::HasResult::Middleware,

ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Middleware.with(fallback_true_status: :failure),
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveParentResult::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::RaisesOnNotResultReturnValue::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanBeMethodStep::CanBeExecuted::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Middleware.with(fallback_true_status: :failure)
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanBeMethodStep::CanBeExecuted::Middleware
]
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,10 +534,10 @@
ConvenientService::Common::Plugins::CachesReturnValue::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::HasResult::Middleware,

ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Middleware.with(fallback_true_status: :failure),
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveParentResult::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::RaisesOnNotResultReturnValue::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanBeMethodStep::CanBeExecuted::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Middleware.with(fallback_true_status: :failure)
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanBeMethodStep::CanBeExecuted::Middleware
]
end

Expand Down
4 changes: 2 additions & 2 deletions spec/lib/convenient_service/service/configs/standard_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -526,10 +526,10 @@
ConvenientService::Common::Plugins::CachesReturnValue::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::HasResult::Middleware,

ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Middleware.with(fallback_true_status: :failure),
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveParentResult::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::RaisesOnNotResultReturnValue::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanBeMethodStep::CanBeExecuted::Middleware,
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Middleware.with(fallback_true_status: :failure)
ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanBeMethodStep::CanBeExecuted::Middleware
]
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

require "spec_helper"

require "convenient_service"

RSpec.describe ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::CanHaveFallbacks::Exceptions, type: :standard do
include ConvenientService::RSpec::PrimitiveMatchers::BeDescendantOf

specify { expect(described_class::FallbackResultIsNotOverridden).to be_descendant_of(ConvenientService::Exception) }
specify { expect(described_class::MethodStepCanNotHaveFallback).to be_descendant_of(ConvenientService::Exception) }
end
Loading

0 comments on commit f90abdd

Please sign in to comment.