From f51707bd1aa0ef3585b620bcffc98a063fcd6b8d Mon Sep 17 00:00:00 2001
From: Ken Mayer <ken@pactapp.com>
Date: Wed, 5 Mar 2014 22:08:55 -0800
Subject: [PATCH 1/3] Filter request and/or response headers by configuration

New configuration variables:
- response_headers_to_include
- request_headers_to_include
---
 README.md                                           |  8 +++++++-
 features/html_documentation.feature                 |  6 +++++-
 features/step_definitions/html_steps.rb             |  9 +++++++++
 lib/rspec_api_documentation/configuration.rb        |  2 ++
 lib/rspec_api_documentation/views/markup_example.rb | 11 +++++++++--
 spec/configuration_spec.rb                          |  2 ++
 6 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 390bd05d..912a922a 100644
--- a/README.md
+++ b/README.md
@@ -102,7 +102,13 @@ RspecApiDocumentation.configure do |config|
   # Allows you to filter out headers that are not needed in the cURL request,
   # such as "Host" and "Cookie". Set as an array.
   config.curl_headers_to_filter = nil
-  
+
+  # By default, when these settings are nil, all headers are shown,
+  # which is sometimes too chatty. Setting the parameters to an
+  # array of headers will render *only* those headers.
+  config.request_headers_to_include = nil
+  config.response_headers_to_include = nil
+
   # By default examples and resources are ordered by description. Set to true keep
   # the source order.
   config.keep_source_order = false
diff --git a/features/html_documentation.feature b/features/html_documentation.feature
index dfe4ffd1..f3b8be2f 100644
--- a/features/html_documentation.feature
+++ b/features/html_documentation.feature
@@ -21,6 +21,8 @@ Feature: Generate HTML documentation from test examples
       RspecApiDocumentation.configure do |config|
         config.app = App
         config.api_name = "Example API"
+        config.request_headers_to_include = %w[Cookie]
+        config.response_headers_to_include = %w[Content-Type]
       end
 
       resource "Greetings" do
@@ -70,8 +72,9 @@ Feature: Generate HTML documentation from test examples
     And   I navigate to "Greeting your favorite gem"
     Then  I should see the route is "GET /greetings?target=rspec_api_documentation"
     And   I should see the following request headers:
-      | Host   | example.org |
       | Cookie |             |
+    And   I should not see the following request headers:
+      | Host   | example.org |
     And   I should see the following query parameters:
       | target | rspec_api_documentation |
 
@@ -81,6 +84,7 @@ Feature: Generate HTML documentation from test examples
     Then  I should see the response status is "200 OK"
     And   I should see the following response headers:
       | Content-Type   | application/json |
+    And   I should not see the following response headers:
       | Content-Length | 35               |
     And   I should see the following response body:
       """
diff --git a/features/step_definitions/html_steps.rb b/features/step_definitions/html_steps.rb
index 5c812d36..60be4e28 100644
--- a/features/step_definitions/html_steps.rb
+++ b/features/step_definitions/html_steps.rb
@@ -26,6 +26,15 @@
   end
 end
 
+Then /^I should not see the following (request|response) headers:$/ do |part, table|
+  actual_headers = page.find("pre.#{part}.headers").text
+  expected_headers = table.raw.map { |row| row.join(": ") }
+
+  expected_headers.each do |row|
+    actual_headers.should_not include(row.strip)
+  end
+end
+
 Then /^I should see the route is "([^"]*)"$/ do |route|
   page.should have_css(".request.route", :text => route)
 end
diff --git a/lib/rspec_api_documentation/configuration.rb b/lib/rspec_api_documentation/configuration.rb
index 94e99d2c..3afd896e 100644
--- a/lib/rspec_api_documentation/configuration.rb
+++ b/lib/rspec_api_documentation/configuration.rb
@@ -59,6 +59,8 @@ def self.add_setting(name, opts = {})
     add_setting :keep_source_order, :default => false
     add_setting :api_name, :default => "API Documentation"
     add_setting :io_docs_protocol, :default => "http"
+    add_setting :request_headers_to_include, :default => nil
+    add_setting :response_headers_to_include, :default => nil
 
     def client_method=(new_client_method)
       RspecApiDocumentation::DSL::Resource.module_eval <<-RUBY
diff --git a/lib/rspec_api_documentation/views/markup_example.rb b/lib/rspec_api_documentation/views/markup_example.rb
index 40d3236a..406076fc 100644
--- a/lib/rspec_api_documentation/views/markup_example.rb
+++ b/lib/rspec_api_documentation/views/markup_example.rb
@@ -7,6 +7,8 @@ def initialize(example, configuration)
         @example = example
         @host = configuration.curl_host
         @filter_headers = configuration.curl_headers_to_filter
+        @request_headers_to_include = configuration.request_headers_to_include
+        @response_headers_to_include = configuration.response_headers_to_include
         self.template_path = configuration.template_path
       end
 
@@ -30,9 +32,9 @@ def filename
 
       def requests
         super.map do |hash|
-          hash[:request_headers_text] = format_hash(hash[:request_headers])
+          hash[:request_headers_text] = format_hash(filter_hash(hash[:request_headers], @request_headers_to_include))
           hash[:request_query_parameters_text] = format_hash(hash[:request_query_parameters])
-          hash[:response_headers_text] = format_hash(hash[:response_headers])
+          hash[:response_headers_text] = format_hash(filter_hash(hash[:response_headers], @response_headers_to_include))
           if @host
             if hash[:curl].is_a? RspecApiDocumentation::Curl
               hash[:curl] = hash[:curl].output(@host, @filter_headers)
@@ -56,6 +58,11 @@ def format_hash(hash = {})
           "#{k}: #{v}"
         end.join("\n")
       end
+
+      def filter_hash(hash = {}, selection_set = nil)
+        return hash unless selection_set
+        hash.select{ |key, value| selection_set.include?(key) }
+      end
     end
   end
 end
diff --git a/spec/configuration_spec.rb b/spec/configuration_spec.rb
index 3458a361..f72f97c8 100644
--- a/spec/configuration_spec.rb
+++ b/spec/configuration_spec.rb
@@ -54,6 +54,8 @@
     its(:api_name) { should == "API Documentation" }
     its(:client_method) { should == :client }
     its(:io_docs_protocol) { should == "http" }
+    its(:request_headers_to_include) { should be_nil }
+    its(:response_headers_to_include) { should be_nil }
   end
 
   describe "#define_groups" do

From dd71da1ad4c3b93c1952860c502c0504d04f0832 Mon Sep 17 00:00:00 2001
From: Ken Mayer <ken@pactapp.com>
Date: Wed, 5 Mar 2014 22:08:55 -0800
Subject: [PATCH 2/3] Filter request and/or response headers by configuration

New configuration variables:
- response_headers_to_include
- request_headers_to_include
---
 features/combined_json.feature                | 14 ++--
 features/textile_documentation.feature        | 10 +--
 lib/rspec_api_documentation/example.rb        | 24 +++++-
 .../views/markup_example.rb                   | 11 +--
 spec/example_spec.rb                          | 78 +++++++++++++++++++
 5 files changed, 113 insertions(+), 24 deletions(-)

diff --git a/features/combined_json.feature b/features/combined_json.feature
index 6a6082a0..617a95b9 100644
--- a/features/combined_json.feature
+++ b/features/combined_json.feature
@@ -24,6 +24,8 @@ Feature: Combined text
       RspecApiDocumentation.configure do |config|
         config.app = App
         config.format = :combined_json
+        config.request_headers_to_include = %w[Host]
+        config.response_headers_to_include = %w[Content-Type]
       end
 
       resource "Greetings" do
@@ -84,8 +86,7 @@ Feature: Combined text
             "request_path": "/greetings?target=rspec_api_documentation",
             "request_body": null,
             "request_headers": {
-              "Host": "example.org",
-              "Cookie": ""
+              "Host": "example.org"
             },
             "request_query_parameters": {
               "target": "rspec_api_documentation"
@@ -95,8 +96,7 @@ Feature: Combined text
             "response_status_text": "OK",
             "response_body": "Hello, rspec_api_documentation!",
             "response_headers": {
-              "Content-Type": "text/plain",
-              "Content-Length": "31"
+              "Content-Type": "text/plain"
             },
             "response_content_type": "text/plain",
             "curl": null
@@ -121,8 +121,7 @@ Feature: Combined text
             "request_path": "/greetings?target=Sam+%26+Eric",
             "request_body": null,
             "request_headers": {
-              "Host": "example.org",
-              "Cookie": ""
+              "Host": "example.org"
             },
             "request_query_parameters": {
               "target": "Sam & Eric"
@@ -132,8 +131,7 @@ Feature: Combined text
             "response_status_text": "OK",
             "response_body": "Hello, Sam & Eric!",
             "response_headers": {
-              "Content-Type": "text/plain",
-              "Content-Length": "18"
+              "Content-Type": "text/plain"
             },
             "response_content_type": "text/plain",
             "curl": null
diff --git a/features/textile_documentation.feature b/features/textile_documentation.feature
index 9ddf25c0..1e4ebff8 100644
--- a/features/textile_documentation.feature
+++ b/features/textile_documentation.feature
@@ -45,6 +45,8 @@ Feature: Generate Textile documentation from test examples
         config.app = App
         config.api_name = "Example API"
         config.format = :textile
+        config.request_headers_to_include = %w[Content-Type Host]
+        config.response_headers_to_include = %w[Content-Type Content-Length]
       end
 
       resource 'Orders' do
@@ -180,8 +182,7 @@ Feature: Generate Textile documentation from test examples
     h4. Headers
 
     <pre>Host: example.org
-    Content-Type: application/x-www-form-urlencoded
-    Cookie: </pre>
+    Content-Type: application/x-www-form-urlencoded</pre>
 
     h4. Route
 
@@ -198,10 +199,7 @@ Feature: Generate Textile documentation from test examples
     h4. Headers
 
     <pre>Content-Type: text/html;charset=utf-8
-    Content-Length: 0
-    X-XSS-Protection: 1; mode=block
-    X-Content-Type-Options: nosniff
-    X-Frame-Options: SAMEORIGIN</pre>
+    Content-Length: 0</pre>
 
     h4. Status
 
diff --git a/lib/rspec_api_documentation/example.rb b/lib/rspec_api_documentation/example.rb
index 6062d7d9..7cd7d5d6 100644
--- a/lib/rspec_api_documentation/example.rb
+++ b/lib/rspec_api_documentation/example.rb
@@ -43,7 +43,29 @@ def explanation
     end
 
     def requests
-      metadata[:requests] || []
+      filter_headers(metadata[:requests]) || []
+    end
+
+    private
+
+    def filter_headers(requests)
+      requests = remap_headers(requests, :request_headers, configuration.request_headers_to_include)
+      requests = remap_headers(requests, :response_headers, configuration.response_headers_to_include)
+      requests
+    end
+
+    def remap_headers(requests, key, headers_to_include)
+      requests.each.with_index do |request_hash, index|
+        next unless request_hash.key?(key)
+        headers = request_hash[key]
+        request_hash[key] = filter_hash(headers, headers_to_include)
+        requests[index] = request_hash
+      end
+    end
+
+    def filter_hash(hash = {}, selection_set = nil)
+      return hash unless selection_set
+      hash.select{ |key, _| selection_set.include?(key) }
     end
   end
 end
diff --git a/lib/rspec_api_documentation/views/markup_example.rb b/lib/rspec_api_documentation/views/markup_example.rb
index 406076fc..40d3236a 100644
--- a/lib/rspec_api_documentation/views/markup_example.rb
+++ b/lib/rspec_api_documentation/views/markup_example.rb
@@ -7,8 +7,6 @@ def initialize(example, configuration)
         @example = example
         @host = configuration.curl_host
         @filter_headers = configuration.curl_headers_to_filter
-        @request_headers_to_include = configuration.request_headers_to_include
-        @response_headers_to_include = configuration.response_headers_to_include
         self.template_path = configuration.template_path
       end
 
@@ -32,9 +30,9 @@ def filename
 
       def requests
         super.map do |hash|
-          hash[:request_headers_text] = format_hash(filter_hash(hash[:request_headers], @request_headers_to_include))
+          hash[:request_headers_text] = format_hash(hash[:request_headers])
           hash[:request_query_parameters_text] = format_hash(hash[:request_query_parameters])
-          hash[:response_headers_text] = format_hash(filter_hash(hash[:response_headers], @response_headers_to_include))
+          hash[:response_headers_text] = format_hash(hash[:response_headers])
           if @host
             if hash[:curl].is_a? RspecApiDocumentation::Curl
               hash[:curl] = hash[:curl].output(@host, @filter_headers)
@@ -58,11 +56,6 @@ def format_hash(hash = {})
           "#{k}: #{v}"
         end.join("\n")
       end
-
-      def filter_hash(hash = {}, selection_set = nil)
-        return hash unless selection_set
-        hash.select{ |key, value| selection_set.include?(key) }
-      end
     end
   end
 end
diff --git a/spec/example_spec.rb b/spec/example_spec.rb
index 858c44a2..932b1331 100644
--- a/spec/example_spec.rb
+++ b/spec/example_spec.rb
@@ -159,4 +159,82 @@
       example.explanation.should == nil
     end
   end
+
+  describe "request headers can be filtered" do
+    before do
+      configuration.request_headers_to_include = %w[Included]
+      metadata[:requests] = [
+          {
+              :request_headers => {
+                  "Included" => "data",
+                  "Filtered" => "not seen"
+              },
+              :request_method => "GET"
+          },
+          {
+              :request_headers => {
+                  "Included" => "data",
+                  "Other" => "not seen"
+              },
+              :request_method => "GET"
+          }
+      ]
+    end
+
+    it "should filter out anything not explicitly mentioned" do
+      subject.requests.should == [
+          {
+              :request_headers => {
+                  "Included" => "data",
+              },
+              :request_method => "GET"
+          },
+          {
+              :request_headers => {
+                  "Included" => "data",
+              },
+              :request_method => "GET"
+          }
+      ]
+    end
+  end
+
+  describe "response headers can be filtered" do
+    before do
+      configuration.response_headers_to_include = %w[Included]
+      metadata[:requests] = [
+          {
+              :response_headers => {
+                  "Included" => "data",
+                  "Filtered" => "not seen"
+              },
+              :request_method => "GET"
+          },
+          {
+              :response_headers => {
+                  "Included" => "data",
+                  "Other" => "not seen"
+              },
+              :request_method => "GET"
+          }
+      ]
+    end
+
+    it "should filter out anything not explicitly mentioned" do
+      subject.requests.should == [
+          {
+              :response_headers => {
+                  "Included" => "data",
+              },
+              :request_method => "GET"
+          },
+          {
+              :response_headers => {
+                  "Included" => "data",
+              },
+              :request_method => "GET"
+          }
+      ]
+    end
+  end
 end

From b2172684504dfb53c3690f2129b5b9b7e2f2aebd Mon Sep 17 00:00:00 2001
From: Ken Mayer <ken@pactapp.com>
Date: Thu, 6 Mar 2014 12:14:26 -0800
Subject: [PATCH 3/3] Refactor

---
 lib/rspec_api_documentation/example.rb | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/lib/rspec_api_documentation/example.rb b/lib/rspec_api_documentation/example.rb
index 7cd7d5d6..da7de75a 100644
--- a/lib/rspec_api_documentation/example.rb
+++ b/lib/rspec_api_documentation/example.rb
@@ -55,17 +55,13 @@ def filter_headers(requests)
     end
 
     def remap_headers(requests, key, headers_to_include)
+      return requests unless headers_to_include
       requests.each.with_index do |request_hash, index|
         next unless request_hash.key?(key)
         headers = request_hash[key]
-        request_hash[key] = filter_hash(headers, headers_to_include)
+        request_hash[key] = headers.select{ |key, _| headers_to_include.include?(key) }
         requests[index] = request_hash
       end
     end
-
-    def filter_hash(hash = {}, selection_set = nil)
-      return hash unless selection_set
-      hash.select{ |key, _| selection_set.include?(key) }
-    end
   end
 end