From 531ab3ab1af09e3abb28f4d53662995e2ee3fdbf Mon Sep 17 00:00:00 2001 From: Beth Skurrie Date: Thu, 8 Mar 2018 14:58:36 +1100 Subject: [PATCH] feat: allow different consumer contract parsers to be registered --- .../consumer_contract/consumer_contract.rb | 27 ++-- .../http_consumer_contract_parser.rb | 26 ++++ lib/pact/consumer_contract/interaction.rb | 4 - lib/pact/consumer_contract/message.rb | 146 ------------------ lib/pact/consumer_contract/message/content.rb | 61 -------- lib/pact/errors.rb | 3 - .../consumer_contract_spec.rb | 22 --- .../message_spec_with_message_class.rb | 25 --- .../message_spec_with_message_module.rb | 25 --- tasks/spec.rake | 10 +- 10 files changed, 39 insertions(+), 310 deletions(-) create mode 100644 lib/pact/consumer_contract/http_consumer_contract_parser.rb delete mode 100644 lib/pact/consumer_contract/message.rb delete mode 100644 lib/pact/consumer_contract/message/content.rb delete mode 100644 spec/lib/pact/consumer_contract/message_spec_with_message_class.rb delete mode 100644 spec/lib/pact/consumer_contract/message_spec_with_message_module.rb diff --git a/lib/pact/consumer_contract/consumer_contract.rb b/lib/pact/consumer_contract/consumer_contract.rb index 3e37212..91b05f0 100644 --- a/lib/pact/consumer_contract/consumer_contract.rb +++ b/lib/pact/consumer_contract/consumer_contract.rb @@ -9,11 +9,10 @@ require 'pact/consumer_contract/service_consumer' require 'pact/consumer_contract/service_provider' require 'pact/consumer_contract/interaction' -require 'pact/consumer_contract/message' require 'pact/consumer_contract/pact_file' +require 'pact/consumer_contract/http_consumer_contract_parser' module Pact - class ConsumerContract include SymbolizeKeys @@ -30,21 +29,19 @@ def initialize(attributes = {}) @provider = attributes[:provider] end + def self.add_parser consumer_contract_parser + parsers << consumer_contract_parser + end + + def self.parsers + @parsers ||= [Pact::HttpConsumerContractParser.new] + end + def self.from_hash(hash) - hash = symbolize_keys(hash) - interactions = if hash[:interactions] - hash[:interactions].collect { |hash| Interaction.from_hash(hash)} - elsif hash[:messages] - hash[:messages].collect { |hash| Message.from_hash(hash)} - else - [] # or raise an error? + parsers.each do | parser | + return parser.call(hash) if parser.can_parse?(hash) end - - new( - :consumer => ServiceConsumer.from_hash(hash[:consumer]), - :provider => ServiceProvider.from_hash(hash[:provider]), - :interactions => interactions - ) + raise Pact::Error.new("No consumer contract parser found for hash: #{hash}") end def self.from_json string diff --git a/lib/pact/consumer_contract/http_consumer_contract_parser.rb b/lib/pact/consumer_contract/http_consumer_contract_parser.rb new file mode 100644 index 0000000..c4154fe --- /dev/null +++ b/lib/pact/consumer_contract/http_consumer_contract_parser.rb @@ -0,0 +1,26 @@ +module Pact + class HttpConsumerContractParser + include SymbolizeKeys + + def call(hash) + hash = symbolize_keys(hash) + interactions = if hash[:interactions] + hash[:interactions].collect { |hash| Interaction.from_hash(hash)} + elsif hash[:messages] + hash[:messages].collect { |hash| Message.from_hash(hash)} + else + [] # or raise an error? + end + + ConsumerContract.new( + :consumer => ServiceConsumer.from_hash(hash[:consumer]), + :provider => ServiceProvider.from_hash(hash[:provider]), + :interactions => interactions + ) + end + + def can_parse?(hash) + hash.key?('interactions') || hash.key?(:interactions) + end + end +end diff --git a/lib/pact/consumer_contract/interaction.rb b/lib/pact/consumer_contract/interaction.rb index 297ab83..dc476a3 100644 --- a/lib/pact/consumer_contract/interaction.rb +++ b/lib/pact/consumer_contract/interaction.rb @@ -40,10 +40,6 @@ def http? true end - def message? - false - end - def validate! raise Pact::InvalidInteractionError.new(self) unless description && request && response end diff --git a/lib/pact/consumer_contract/message.rb b/lib/pact/consumer_contract/message.rb deleted file mode 100644 index 87c91e3..0000000 --- a/lib/pact/consumer_contract/message.rb +++ /dev/null @@ -1,146 +0,0 @@ -require 'pact/consumer_contract/message/content' -require 'pact/symbolize_keys' -require 'pact/shared/active_support_support' -require 'pact/matching_rules' -require 'pact/errors' -require 'pact/consumer/request' -require 'pact/consumer_contract/response' -require 'pact/consumer_contract/message/content' - -module Pact - class ConsumerContract - class Message - include Pact::ActiveSupportSupport - include Pact::SymbolizeKeys - - attr_accessor :description, :content, :provider_state - - def initialize attributes = {} - @description = attributes[:description] - @provider_state = attributes[:provider_state] || attributes[:providerState] - @content = attributes[:content] - end - - def self.from_hash hash - content_hash = Pact::MatchingRules.merge(hash['content'], hash['content']['matchingRules']) - content = Pact::ConsumerContract::Message::Content.new(content_hash) - new(symbolize_keys(hash).merge(content: content)) - end - - def to_hash - { - description: description, - provider_state: provider_state, - content: content.to_hash, - } - end - - # todo move this proper decorator - def as_json - { - description: description, - providerState: provider_state, - content: content.as_json - } - end - - def request - @request ||= Pact::Consumer::Request::Actual.from_hash( - path: '/', - method: 'POST', - query: nil, - headers: {'Content-Type' => 'application/json'}, - body: { - description: description, - providerStates: [{ - name: provider_state - }] - } - ) - end - - # custom media type? - def response - @response ||= Pact::Response.new( - status: 200, - headers: {'Content-Type' => 'application/json'}, - body: { - content: content - } - ) - end - - def http? - false - end - - def message? - true - end - - def validate! - raise Pact::InvalidMessageError.new(self) unless description && content - end - - def == other - other.is_a?(Message) && to_hash == other.to_hash - end - - def matches_criteria? criteria - criteria.each do | key, value | - unless match_criterion self.send(key.to_s), value - return false - end - end - true - end - - def match_criterion target, criterion - target == criterion || (criterion.is_a?(Regexp) && criterion.match(target)) - end - - def eq? other - self == other - end - - def description_with_provider_state_quoted - provider_state ? "\"#{description}\" given \"#{provider_state}\"" : "\"#{description}\"" - end - - def to_s - to_hash.to_s - end - end - end -end - -# I'm not sure whether to make Pact::Message a module or a class at this stage, so making -# the "public interface" to the pact-support library support Pact::Message.new either way - -if Pact.const_defined?('Message') && Pact::Message.class == Module - module Pact - module Message - def self.new *args - Pact::ConsumerContract::Message.new(*args) - end - - def self.from_hash *args - Pact::ConsumerContract::Message.from_hash(*args) - end - end - end -end - -if Pact.const_defined?('Message') && Pact::Message.class == Class - module Pact - class Message - def self.new *args - Pact::ConsumerContract::Message.new(*args) - end - - def self.from_hash *args - Pact::ConsumerContract::Message.from_hash(*args) - end - end - end -end diff --git a/lib/pact/consumer_contract/message/content.rb b/lib/pact/consumer_contract/message/content.rb deleted file mode 100644 index e6e4a1c..0000000 --- a/lib/pact/consumer_contract/message/content.rb +++ /dev/null @@ -1,61 +0,0 @@ -module Pact - class ConsumerContract - class Message - class Content - include ActiveSupportSupport - include SymbolizeKeys - - def initialize content - @content = content - end - - def to_s - if @content.is_a?(Hash) || @content.is_a?(Array) - @content.to_json - else - @content.to_s - end - end - - def as_json - @content - end - end - end - end -end - -# I'm not sure whether to make Pact::Message a module or a class at this stage, so making -# the "public interface" to the pact-support library support Pact::Message.new either way - -if Pact.const_defined?('Message') && Pact::Message.class == Module - module Pact - module Message - class Content - def self.new *args - Pact::ConsumerContract::Message::Content.new(*args) - end - - def self.from_hash *args - Pact::ConsumerContract::Message::Content.from_hash(*args) - end - end - end - end -end - -if Pact.const_defined?('Message') && Pact::Message.class == Class - module Pact - class Message - class Content - def self.new *args - Pact::ConsumerContract::Message::Content.new(*args) - end - end - - def self.from_hash *args - Pact::ConsumerContract::Message::Content.from_hash(*args) - end - end - end -end diff --git a/lib/pact/errors.rb b/lib/pact/errors.rb index d48ee32..c591895 100644 --- a/lib/pact/errors.rb +++ b/lib/pact/errors.rb @@ -2,9 +2,6 @@ module Pact class Error < ::StandardError end - class InvalidMessageError < Error - end - # Raised when the interaction is not defined correctly class InvalidInteractionError < Error def initialize(interaction) diff --git a/spec/lib/pact/consumer_contract/consumer_contract_spec.rb b/spec/lib/pact/consumer_contract/consumer_contract_spec.rb index 78a6070..18c1609 100644 --- a/spec/lib/pact/consumer_contract/consumer_contract_spec.rb +++ b/spec/lib/pact/consumer_contract/consumer_contract_spec.rb @@ -52,28 +52,6 @@ module Pact end end end - - context "with a Message contract" do - let(:string) { '{"messages":[{"content": {"foo": "bar"}}], "consumer": {"name" : "Bob"} , "provider": {"name" : "Mary"}}' } - - it "should create a Pact" do - expect(loaded_pact).to be_instance_of ConsumerContract - end - - it "should have messages" do - expect(loaded_pact.interactions).to be_instance_of Array - expect(loaded_pact.interactions.first).to be_instance_of Pact::ConsumerContract::Message - end - - it "should have a consumer" do - expect(loaded_pact.consumer).to be_instance_of Pact::ServiceConsumer - end - - it "should have a provider" do - expect(loaded_pact.provider).to be_instance_of Pact::ServiceProvider - end - - end end describe "find_interactions" do diff --git a/spec/lib/pact/consumer_contract/message_spec_with_message_class.rb b/spec/lib/pact/consumer_contract/message_spec_with_message_class.rb deleted file mode 100644 index 1f28e41..0000000 --- a/spec/lib/pact/consumer_contract/message_spec_with_message_class.rb +++ /dev/null @@ -1,25 +0,0 @@ -# I'm not sure whether to make Pact::Message a module or a class at this stage, so making -# the "public interface" to the pact-support library support Pact::Message.new either way - -module Pact - class Message; end -end - -load 'pact/consumer_contract/message.rb' -load 'pact/consumer_contract/message/content.rb' - -describe Pact::Message::Content do - describe "new" do - it "returns an instance of Pact::Message::ConsumerContract::Message::Content" do - expect(Pact::Message::Content.new('foo')).to be_a(Pact::ConsumerContract::Message::Content) - end - end -end - -describe Pact::Message do - describe "new" do - it "returns an instance of Pact::Message::ConsumerContract::Message" do - expect(Pact::Message.new).to be_a(Pact::ConsumerContract::Message) - end - end -end diff --git a/spec/lib/pact/consumer_contract/message_spec_with_message_module.rb b/spec/lib/pact/consumer_contract/message_spec_with_message_module.rb deleted file mode 100644 index 60ea365..0000000 --- a/spec/lib/pact/consumer_contract/message_spec_with_message_module.rb +++ /dev/null @@ -1,25 +0,0 @@ -# I'm not sure whether to make Pact::Message a module or a class at this stage, so making -# the "public interface" to the pact-support library support Pact::Message.new either way - -module Pact - module Message; end -end - -load 'pact/consumer_contract/message.rb' -load 'pact/consumer_contract/message/content.rb' - -describe Pact::Message::Content do - describe "new" do - it "returns an instance of Pact::Message::ConsumerContract::Message::Content" do - expect(Pact::Message::Content.new('foo')).to be_a(Pact::ConsumerContract::Message::Content) - end - end -end - -describe Pact::Message do - describe "new" do - it "returns an instance of Pact::Message::ConsumerContract::Message" do - expect(Pact::Message.new).to be_a(Pact::ConsumerContract::Message) - end - end -end diff --git a/tasks/spec.rake b/tasks/spec.rake index 7bc3d2a..722a5d8 100644 --- a/tasks/spec.rake +++ b/tasks/spec.rake @@ -2,14 +2,6 @@ require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) -RSpec::Core::RakeTask.new(:spec_with_message_class) do | task | - task.pattern = "spec/lib/pact/consumer_contract/message_spec_with_message_class.rb" -end - -RSpec::Core::RakeTask.new(:spec_with_message_module) do | task | - task.pattern = "spec/lib/pact/consumer_contract/message_spec_with_message_module.rb" -end - task :set_active_support_on do ENV["LOAD_ACTIVE_SUPPORT"] = 'true' end @@ -19,4 +11,4 @@ task :spec_with_active_support => [:set_active_support_on] do Rake::Task['spec'].execute end -task :default => [:spec, :spec_with_active_support, :spec_with_message_class, :spec_with_message_module] +task :default => [:spec, :spec_with_active_support]