Skip to content

Commit

Permalink
feat(using_dry_validation): allow to pass status to middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
marian13 committed Sep 11, 2023
1 parent bc5bb70 commit 3a5ef12
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,47 @@ module UsingDryValidation
class Middleware < MethodChainMiddleware
intended_for :result, entity: :service

##
# @return [ConvenientService::Service::Plugins::HasJSendResult::Entities::Result]
#
def next(...)
return entity.failure(data: errors, message: errors.first.to_a.map(&:to_s).join(" ")) if errors.any?
return entity.public_send(status, data: errors, message: errors.first.to_a.map(&:to_s).join(" ")) if errors.any?

chain.next(...)
end

private

##
# NOTE: An example of `Dry::Validation::Contract` usage.
# https://dry-rb.org/gems/dry-validation/1.8/#quick-start
# @return [Hash{Symbol => Object}]
#
# TODO: Return one or all errors?
# @internal
# NOTE: An example of `Dry::Validation::Contract` usage.
# https://dry-rb.org/gems/dry-validation/1.8/#quick-start
#
# TODO: Return one or all errors?
#
def errors
@errors ||= resolve_errors
end

##
# @return [Hash{Symbol => Object}]
#
def constructor_kwargs
@constructor_kwargs ||= entity.constructor_arguments.kwargs
end

##
# @return [Dry::Validation::Contract]
#
def contract
@contract ||= entity.class.contract
end

##
# @return [Hash{Symbol => Object}]
#
def resolve_errors
return {} unless contract.schema

Expand All @@ -43,6 +58,13 @@ def resolve_errors
.to_h
.transform_values(&:first)
end

##
# @return [Symbol]
#
def status
middleware_arguments.kwargs.fetch(:status) { :failure }
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,57 +32,28 @@
end
end

example_group "instance methods" do
describe "#call" do
include ConvenientService::RSpec::Helpers::WrapMethod
include ConvenientService::RSpec::Matchers::CallChainNext
include ConvenientService::RSpec::Matchers::Results
shared_examples "verify middleware behavior" do
example_group "instance methods" do
describe "#call" do
include ConvenientService::RSpec::Helpers::WrapMethod
include ConvenientService::RSpec::Matchers::CallChainNext
include ConvenientService::RSpec::Matchers::Results

subject(:method_value) { method.call }
subject(:method_value) { method.call }

let(:method) { wrap_method(service_instance, method_name, observe_middleware: middleware) }
let(:method_name) { :result }
let(:method) { wrap_method(service_instance, :result, observe_middleware: middleware.with(status: status)) }

let(:service_class) do
Class.new.tap do |klass|
klass.class_exec(middleware) do |middleware|
include ConvenientService::Standard::Config

concerns do
use ConvenientService::Service::Plugins::HasJSendResultParamsValidations::UsingDryValidation::Concern
end

middlewares :result do
use_and_observe middleware
end

contract do
params do
required(:foo).value(:string, max_size?: 2)
end
end

def result
success
end
end
end
end

let(:service_instance) { service_class.new }

context "when contact does NOT have schema" do
let(:service_class) do
Class.new.tap do |klass|
klass.class_exec(middleware) do |middleware|
klass.class_exec(status, middleware) do |status, middleware|
include ConvenientService::Standard::Config

concerns do
use ConvenientService::Service::Plugins::HasJSendResultParamsValidations::UsingDryValidation::Concern
end

middlewares :result do
use_and_observe middleware
use_and_observe middleware.with(status: status)
end

contract do
Expand All @@ -98,45 +69,127 @@ def result
end
end

before do
service_instance.internals.cache.write(:constructor_arguments, ConvenientService::Support::Arguments.new(foo: "x"))
end
let(:service_instance) { service_class.new }

context "when contact does NOT have schema" do
let(:service_class) do
Class.new.tap do |klass|
klass.class_exec(status, middleware) do |status, middleware|
include ConvenientService::Standard::Config

specify do
expect { method_value }
.to call_chain_next.on(method)
.and_return_its_value
concerns do
use ConvenientService::Service::Plugins::HasJSendResultParamsValidations::UsingDryValidation::Concern
end

middlewares :result do
use_and_observe middleware.with(status: status)
end

contract do
params do
required(:foo).value(:string, max_size?: 2)
end
end

def result
success
end
end
end
end

before do
service_instance.internals.cache.write(:constructor_arguments, ConvenientService::Support::Arguments.new(foo: "x"))
end

specify do
expect { method_value }
.to call_chain_next.on(method)
.and_return_its_value
end
end
end

context "when validation does NOT have any errors" do
before do
service_instance.internals.cache.write(:constructor_arguments, ConvenientService::Support::Arguments.new(foo: "x"))
context "when validation does NOT have any errors" do
before do
service_instance.internals.cache.write(:constructor_arguments, ConvenientService::Support::Arguments.new(foo: "x"))
end

specify do
expect { method_value }
.to call_chain_next.on(method)
.and_return_its_value
end
end

specify do
expect { method_value }
.to call_chain_next.on(method)
.and_return_its_value
context "when validation has any error" do
before do
service_instance.internals.cache.write(:constructor_arguments, ConvenientService::Support::Arguments.new(foo: "bar"))
end

let(:errors) { service_class.contract.new.call(**service_instance.constructor_arguments.kwargs).errors.to_h.transform_values(&:first) }

it "returns result with first error message for each invalid attribute as data" do
expect(method_value).to be_result(status).with_data(errors)
end

it "returns result with first error key/value joined by space as message" do
expect(method_value).to be_result(status).with_message(errors.first.to_a.map(&:to_s).join(" "))
end
end
end
end
end

context "when validation has any error" do
before do
service_instance.internals.cache.write(:constructor_arguments, ConvenientService::Support::Arguments.new(foo: "bar"))
end
context "when status is NOT passed" do
subject(:method_value) { method.call }

let(:errors) { service_class.contract.new.call(**service_instance.constructor_arguments.kwargs).errors.to_h.transform_values(&:first) }
include ConvenientService::RSpec::Helpers::WrapMethod

it "returns failure with first error message for each invalid attribute as data" do
expect(method_value).to be_failure.with_data(errors).with_message(errors.first.to_a.map(&:to_s).join(" "))
end
let(:method) { wrap_method(service_instance, :result, observe_middleware: middleware) }

let(:service_class) do
Class.new.tap do |klass|
klass.class_exec(middleware) do |middleware|
include ConvenientService::Standard::Config

concerns do
use ConvenientService::Service::Plugins::HasJSendResultParamsValidations::UsingDryValidation::Concern
end

it "returns failure with first error key/value joined by space as message" do
expect(method_value).to be_failure.with_message(errors.first.to_a.map(&:to_s).join(" "))
middlewares :result do
use_and_observe middleware
end

contract do
params do
required(:foo).value(:string, max_size?: 2)
end
end

def result
success
end
end
end
end

let(:service_instance) { service_class.new(foo: "bar") }

it "defaults to `:failure`" do
expect(method_value).to be_failure
end
end

context "when status is failure" do
include_examples "verify middleware behavior" do
let(:status) { :failure }
end
end

context "when status is error" do
include_examples "verify middleware behavior" do
let(:status) { :error }
end
end
end
# rubocop:enable RSpec/NestedGroups, RSpec/MultipleMemoizedHelpers

0 comments on commit 3a5ef12

Please sign in to comment.