Skip to content

Commit

Permalink
refactor: copy pact/doc code into pact_broker repo to assist in break…
Browse files Browse the repository at this point in the history
…ing the depdendency on webrick
  • Loading branch information
bethesque committed Jan 12, 2018
1 parent d78e62d commit 47e282f
Show file tree
Hide file tree
Showing 23 changed files with 1,049 additions and 1 deletion.
5 changes: 5 additions & 0 deletions lib/pact/doc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Pact::Doc

The code in pact/doc has been copied from the pact gem to break the runtime dependency between the pact_broker and the pact gem, which requires the pact_mock-service, which requires webrick.

This was required as there is a vulnerability in webrick <= 1.3.1 that we don't want to include in the pact_broker gem, but webrick > 1.3.1 requires ruby 2.3.0, but we need ruby 2.2 for Travelling Ruby, which we use to package the Ruby codebase so it can be shared with implementations in other langauges.
39 changes: 39 additions & 0 deletions lib/pact/doc/doc_file.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module Pact
module Doc
class DocFile

def initialize consumer_contract, dir, consumer_contract_renderer, file_extension
@dir = dir
@consumer_contract = consumer_contract
@consumer_contract_renderer = consumer_contract_renderer
@file_extension = file_extension
end

def write
File.open(path, "w") { |io| io << doc_file_contents }
end

def title
consumer_contract.provider.name
end

def name
"#{consumer_contract.consumer.name} - #{consumer_contract.provider.name}#{file_extension}"
end

private

attr_reader :dir, :consumer_contract, :consumer_contract_renderer, :file_extension


def path
File.join(dir, name)
end

def doc_file_contents
consumer_contract_renderer.call(consumer_contract)
end

end
end
end
11 changes: 11 additions & 0 deletions lib/pact/doc/generate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Pact
module Doc
class Generate

def self.call pact_dir = Pact.configuration.pact_dir, doc_dir = Pact.configuration.doc_dir, doc_generators = Pact.configuration.doc_generators
doc_generators.each{| doc_generator| doc_generator.call pact_dir, doc_dir }
end

end
end
end
82 changes: 82 additions & 0 deletions lib/pact/doc/generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
require 'pact/doc/doc_file'
require 'fileutils'

module Pact
module Doc

class Generator

def initialize pact_dir, doc_dir, options
@doc_dir = doc_dir
@pact_dir = pact_dir
@consumer_contract_renderer = options[:consumer_contract_renderer]
@doc_type = options[:doc_type]
@file_extension = options[:file_extension]
@index_renderer = options[:index_renderer]
@index_name = options[:index_name]
@after = options.fetch(:after, lambda{|pact_dir, target_dir, consumer_contracts| })
end

def call
ensure_target_dir_exists_and_is_clean
write_index if consumer_contracts.any?
write_doc_files
perform_after_hook
end

private

attr_reader :doc_dir, :pact_dir, :consumer_contract_renderer, :doc_type, :file_extension, :index_renderer, :after

def write_index
File.open(index_file_path, "w") { |io| io << index_file_contents }
end

def index_file_path
File.join(target_dir, "#{@index_name}#{file_extension}")
end

def index_file_contents
index_renderer.call(consumer_contracts.first.consumer.name, index_data)
end

def index_data
doc_files.each_with_object({}) do | doc_file, data |
data[doc_file.title] = doc_file.name
end
end

def write_doc_files
doc_files.each(&:write)
end

def doc_files
consumer_contracts.collect do | consumer_contract |
DocFile.new(consumer_contract, target_dir, consumer_contract_renderer, file_extension)
end
end

def consumer_contracts
@consumer_contracts ||= begin
Dir.glob("#{pact_dir}/**").collect do |file|
Pact::ConsumerContract.from_uri file
end
end
end

def perform_after_hook
after.call(pact_dir, target_dir, consumer_contracts)
end

def ensure_target_dir_exists_and_is_clean
FileUtils.rm_rf target_dir
FileUtils.mkdir_p target_dir
end

def target_dir
File.join(doc_dir, doc_type)
end

end
end
end
124 changes: 124 additions & 0 deletions lib/pact/doc/interaction_view_model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
require 'pact/shared/active_support_support'
require 'pact/reification'
require 'cgi'

module Pact
module Doc
class InteractionViewModel

include Pact::ActiveSupportSupport

def initialize interaction, consumer_contract
@interaction = interaction
@consumer_contract = consumer_contract
end

def id
@id ||= begin
full_desc = if has_provider_state?
"#{description} given #{interaction.provider_state}"
else
description
end
CGI.escapeHTML(full_desc.gsub(/\s+/,'_'))
end
end

def request_method
interaction.request.method.upcase
end

def request_path
interaction.request.path
end

def response_status
interaction.response.status
end

def consumer_name
markdown_escape @consumer_contract.consumer.name
end

def provider_name
markdown_escape @consumer_contract.provider.name
end

def has_provider_state?
@interaction.provider_state && !@interaction.provider_state.empty?
end

def provider_state start_of_sentence = false
markdown_escape apply_capitals(@interaction.provider_state.strip, start_of_sentence)
end

def description start_of_sentence = false
return '' unless @interaction.description
markdown_escape apply_capitals(@interaction.description.strip, start_of_sentence)
end

def request
fix_json_formatting JSON.pretty_generate(clean_request)
end

def response
fix_json_formatting JSON.pretty_generate(clean_response)
end

private

attr_reader :interaction, :consumer_contract

def clean_request
reified_request = Reification.from_term(interaction.request)
ordered_clean_hash(reified_request).tap do | hash |
hash[:body] = reified_request[:body] if reified_request[:body]
end
end

def clean_response
ordered_clean_hash Reification.from_term(interaction.response)
end

# Remove empty body and headers hashes from response, and empty headers from request,
# as an empty hash means "allow anything" - it's more intuitive and cleaner to just
# remove the empty hashes from display.
def ordered_clean_hash source
ordered_keys.each_with_object({}) do |key, target|
if source.key? key
target[key] = source[key] unless value_is_an_empty_hash(source[key])
end
end
end

def value_is_an_empty_hash value
value.is_a?(Hash) && value.empty?
end

def ordered_keys
[:method, :path, :query, :status, :headers, :body]
end

def remove_key_if_empty key, hash
hash.delete(key) if hash[key].is_a?(Hash) && hash[key].empty?
end

def apply_capitals string, start_of_sentence = false
start_of_sentence ? capitalize_first_letter(string) : lowercase_first_letter(string)
end

def capitalize_first_letter string
string[0].upcase + string[1..-1]
end

def lowercase_first_letter string
string[0].downcase + string[1..-1]
end

def markdown_escape string
return nil unless string
string.gsub('*','\*').gsub('_','\_')
end
end
end
end
68 changes: 68 additions & 0 deletions lib/pact/doc/markdown/consumer_contract_renderer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require 'pact/doc/markdown/interaction_renderer'
require 'pact/doc/sort_interactions'

module Pact
module Doc
module Markdown
class ConsumerContractRenderer

def initialize consumer_contract
@consumer_contract = consumer_contract
end

def self.call consumer_contract
new(consumer_contract).call
end

def call
title + summaries_title + summaries.join + interactions_title + full_interactions.join
end

private

attr_reader :consumer_contract

def title
"### A pact between #{consumer_name} and #{provider_name}\n\n"
end

def interaction_renderers
@interaction_renderers ||= sorted_interactions.collect{|interaction| InteractionRenderer.new interaction, @consumer_contract}
end

def summaries_title
"#### Requests from #{consumer_name} to #{provider_name}\n\n"
end

def interactions_title
"#### Interactions\n\n"
end

def summaries
interaction_renderers.collect(&:render_summary)
end

def full_interactions
interaction_renderers.collect(&:render_full_interaction)
end

def sorted_interactions
SortInteractions.call(consumer_contract.interactions)
end

def consumer_name
markdown_escape consumer_contract.consumer.name
end

def provider_name
markdown_escape consumer_contract.provider.name
end

def markdown_escape string
string.gsub('*','\*').gsub('_','\_')
end

end
end
end
end
24 changes: 24 additions & 0 deletions lib/pact/doc/markdown/generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
require 'pact/doc/generator'
require 'pact/doc/markdown/consumer_contract_renderer'
require 'pact/doc/markdown/index_renderer'

module Pact
module Doc
module Markdown
class Generator < Pact::Doc::Generator
def initialize pact_dir, doc_dir
super(pact_dir, doc_dir,
consumer_contract_renderer: ConsumerContractRenderer,
doc_type: 'markdown',
file_extension: '.md',
index_renderer: IndexRenderer,
index_name: 'README')
end

def self.call pact_dir, doc_dir
new(pact_dir, doc_dir).call
end
end
end
end
end
Loading

0 comments on commit 47e282f

Please sign in to comment.