lib/pact/consumer_contract/consumer_contract.rb



require 'pact/consumer_contract'
require 'pact/logging'
require 'pact/json_warning'
require 'date'
require 'pact/version'
require 'open-uri'
require_relative 'service_consumer'
require_relative 'service_provider'
require_relative 'interaction'



module Pact

  #TODO move to external file for reuse
  module FileName
    def file_name consumer_name, provider_name
      "#{filenamify(consumer_name)}-#{filenamify(provider_name)}.json"
    end

    def filenamify name
      name.downcase.gsub(/\s/, '_')
    end
  end

  class ConsumerContract

    include Logging
    include JsonWarning
    include FileName

    attr_accessor :interactions
    attr_accessor :consumer
    attr_accessor :provider

    def initialize(attributes = {})
      @interactions = attributes[:interactions] || []
      @consumer = attributes[:consumer]
      @provider = attributes[:provider]
    end

    def as_json(options = {})
      {
        provider: @provider.as_json,
        consumer: @consumer.as_json,
        interactions: @interactions.collect(&:as_json),
        metadata: {
          pact_gem: {
            version: Pact::VERSION
          }
        }
      }
    end

    def to_json(options = {})
      as_json(options).to_json(
        )
    end

    def self.from_hash(obj)
      new({
        :interactions => obj['interactions'].collect { |hash| Interaction.from_hash(hash)},
        :consumer => ServiceConsumer.from_hash(obj['consumer']),
        :provider => ServiceProvider.from_hash(obj['provider'] || {})
      })
    end

    def self.from_json string
      deserialised_object = JSON.load(maintain_backwards_compatiblity_with_producer_keys(string))
      from_hash(deserialised_object)
    end

    def self.from_uri uri
      from_json(open(uri){ |f| f.read })
    end

    def self.maintain_backwards_compatiblity_with_producer_keys string
      string.gsub('"producer":', '"provider":').gsub('"producer_state":', '"provider_state":')
    end

    def find_interaction criteria
      interactions = find_interactions criteria
      if interactions.size == 0
        raise "Could not find interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}."
      elsif interactions.size > 1
        raise "Found more than 1 interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}."
      end
      interactions.first
    end

    def find_interactions criteria
      interactions.select{ | interaction| interaction.matches_criteria?(criteria)}
    end

    def each
      interactions.each do | interaction |
        yield interaction
      end
    end

    def pact_file_name
      file_name consumer.name, provider.name
    end

    def pactfile_path
      raise 'You must first specify a consumer and service name' unless (consumer && consumer.name && provider && provider.name)
      @pactfile_path ||= File.join(Pact.configuration.pact_dir, pact_file_name)
    end

    def update_pactfile
      logger.debug "Updating pact file for #{provider.name} at #{pactfile_path}"
      check_for_active_support_json
      File.open(pactfile_path, 'w') do |f|
        f.write JSON.pretty_generate(self)
      end
    end
  end
end