Skip to content

Commit

Permalink
fix(step): respect aliases for last(all) steps
Browse files Browse the repository at this point in the history
  • Loading branch information
marian13 committed May 9, 2024
1 parent 61b0845 commit b8285e3
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -399,11 +399,10 @@ def calculate_input_values
# TODO: Create `OutputMethod`. Move `validate_as_output_for_result` into it.
#
def calculate_output_values
return {} unless status.unsafe_success?
return {} if status.unsafe_not_success?
return {} if outputs.none?

outputs.each { |output| ::ConvenientService.raise Exceptions::StepResultDataNotExistingAttribute.new(step: self, key: output.key.to_sym) unless unsafe_data.has_attribute?(output.key.to_sym) }

outputs.reduce({}) { |values, output| values.merge(output.name.to_sym => unsafe_data[output.key.to_sym]) }
unsafe_data.to_h
end

##
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ module HasResult
class Middleware < MethodChainMiddleware
intended_for :result, entity: :step

##
# @return [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step]
#
alias_method :step, :entity

##
# @return [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
# @raise [ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Exceptions::StepHasNoOrganizer]
Expand All @@ -20,7 +25,27 @@ class Middleware < MethodChainMiddleware
# TODO: `service.result`.
#
def next(...)
chain.next(...).copy(overrides: {kwargs: {step: entity, service: entity.organizer}})
result = chain.next(...)

result.copy(overrides: {kwargs: {data: extract_data(result), step: entity, service: entity.organizer}})
end

private

##
# @param result [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
# @return [Hash]
#
# @internal
# TODO: Remove `step.outputs.none?` to enforce users to specify outputs for all steps. Will be a breaking change.
#
def extract_data(result)
return result.unsafe_data.to_h if result.status.unsafe_not_success?
return result.unsafe_data.to_h if step.outputs.none?

step.outputs.each { |output| ::ConvenientService.raise Exceptions::StepResultDataNotExistingAttribute.new(step: step, key: output.key.to_sym) unless result.unsafe_data.has_attribute?(output.key.to_sym) }

step.outputs.reduce({}) { |values, output| values.merge(output.name.to_sym => result.unsafe_data[output.key.to_sym]) }
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def result
end

it "returns corresponding step service result data attribute by key" do
expect(organizer.bar).to eq(organizer.steps[0].result.unsafe_data[:foo])
expect(organizer.bar).to eq(organizer.steps[0].result.unsafe_data[:bar])
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ def foo

specify do
expect { step.output_values }
.to delegate_to(step.status, :unsafe_success?)
.to delegate_to(step.status, :unsafe_not_success?)
.without_arguments
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

# rubocop:disable RSpec/NestedGroups, RSpec/MultipleMemoizedHelpers
RSpec.describe ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Plugins::HasResult::Middleware do
include ConvenientService::RSpec::Helpers::IgnoringException
include ConvenientService::RSpec::Matchers::DelegateTo

let(:middleware) { described_class }

example_group "inheritance" do
Expand Down Expand Up @@ -65,7 +68,7 @@
include ConvenientService::Service::Configs::Essential

def result
success(data: {foo: :bar})
success(data: {foo: :foo, bar: :bar, baz: :baz})
end
end
end
Expand All @@ -77,7 +80,213 @@ def result
end

it "returns service result with step and organizer" do
expect(step.result).to be_success.with_data(foo: :bar).of_step(first_step).of_service(container)
expect(step.result).to be_success.with_data(foo: :foo, bar: :bar, baz: :baz).of_step(first_step).of_service(container)
end

context "when `step` result has success status" do
context "when `step` has no outputs" do
let(:container) do
Class.new.tap do |klass|
klass.class_exec(first_step, middleware) do |first_step, middleware|
include ConvenientService::Service::Configs::Essential

self::Step.class_exec(middleware) do |middleware|
middlewares :result do
observe middleware
end
end

step first_step
end
end
end

it "returns service result with original data keys" do
expect(step.result).to be_success.with_data(foo: :foo, bar: :bar, baz: :baz).of_step(first_step).of_service(container)
end
end

context "when `step` has outputs" do
context "when `step` has one output" do
context "when step result does NOT have attribute by that output key" do
let(:container) do
Class.new.tap do |klass|
klass.class_exec(first_step, middleware) do |first_step, middleware|
include ConvenientService::Service::Configs::Essential

self::Step.class_exec(middleware) do |middleware|
middlewares :result do
observe middleware
end
end

step first_step, out: :qux
end
end
end

let(:exception_message) do
<<~TEXT
Step `#{step.printable_service}` result does NOT return `:qux` data attribute.
Maybe there is a typo in `out` definition?
Or `success` of `#{step.printable_service}` accepts a wrong key?
TEXT
end

it "raises `ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Exceptions::StepResultDataNotExistingAttribute`" do
expect { step.result }
.to raise_error(ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Exceptions::StepResultDataNotExistingAttribute)
.with_message(exception_message)
end

specify do
expect { ignoring_exception(ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Exceptions::StepResultDataNotExistingAttribute) { step.result } }
.to delegate_to(ConvenientService, :raise)
end
end

context "when step result has attribute by that output key" do
let(:container) do
Class.new.tap do |klass|
klass.class_exec(first_step, middleware) do |first_step, middleware|
include ConvenientService::Service::Configs::Essential

self::Step.class_exec(middleware) do |middleware|
middlewares :result do
observe middleware
end
end

step first_step, out: :foo
end
end
end

it "returns service result only with that output data key" do
expect(step.result).to be_success.with_data(foo: :foo).of_step(first_step).of_service(container)
end
end
end

context "when `step` has multiple outputs" do
context "when step result does NOT have any attribute by those output keys" do
let(:container) do
Class.new.tap do |klass|
klass.class_exec(first_step, middleware) do |first_step, middleware|
include ConvenientService::Service::Configs::Essential

self::Step.class_exec(middleware) do |middleware|
middlewares :result do
observe middleware
end
end

step first_step, out: [:baz, :qux]
end
end
end

let(:exception_message) do
<<~TEXT
Step `#{step.printable_service}` result does NOT return `:qux` data attribute.
Maybe there is a typo in `out` definition?
Or `success` of `#{step.printable_service}` accepts a wrong key?
TEXT
end

it "raises `ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Exceptions::StepResultDataNotExistingAttribute`" do
expect { step.result }
.to raise_error(ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Exceptions::StepResultDataNotExistingAttribute)
.with_message(exception_message)
end

specify do
expect { ignoring_exception(ConvenientService::Service::Plugins::CanHaveSteps::Entities::Step::Exceptions::StepResultDataNotExistingAttribute) { step.result } }
.to delegate_to(ConvenientService, :raise)
end
end

context "when step result has all attributes by those output keys" do
let(:container) do
Class.new.tap do |klass|
klass.class_exec(first_step, middleware) do |first_step, middleware|
include ConvenientService::Service::Configs::Essential

self::Step.class_exec(middleware) do |middleware|
middlewares :result do
observe middleware
end
end

step first_step, out: [:foo, :bar]
end
end
end

it "returns service result only with those output data keys" do
expect(step.result).to be_success.with_data(foo: :foo, bar: :bar).of_step(first_step).of_service(container)
end
end
end

context "when `step` has any output with alias" do
let(:container) do
Class.new.tap do |klass|
klass.class_exec(first_step, middleware) do |first_step, middleware|
include ConvenientService::Service::Configs::Essential

self::Step.class_exec(middleware) do |middleware|
middlewares :result do
observe middleware
end
end

step first_step, out: [{foo: :abc}, :bar]
end
end
end

it "returns service result only with outputs data keys respecting aliases" do
expect(step.result).to be_success.with_data(abc: :foo, bar: :bar).of_step(first_step).of_service(container)
end
end
end
end

context "when `step` result has failure status" do
let(:first_step) do
Class.new do
include ConvenientService::Service::Configs::Essential

def result
failure(data: {foo: :foo, bar: :bar, baz: :baz})
end
end
end

it "returns service result with original data keys" do
expect(step.result).to be_failure.with_data(foo: :foo, bar: :bar, baz: :baz).of_step(first_step).of_service(container)
end
end

context "when `step` result has error status" do
let(:first_step) do
Class.new do
include ConvenientService::Service::Configs::Essential

def result
error(data: {foo: :foo, bar: :bar, baz: :baz})
end
end
end

it "returns service result with original data keys" do
expect(step.result).to be_error.with_data(foo: :foo, bar: :bar, baz: :baz).of_step(first_step).of_service(container)
end
end
end
end
Expand Down

0 comments on commit b8285e3

Please sign in to comment.