Skip to content

Commit

Permalink
Merge pull request #2112 from tf/additional-headers
Browse files Browse the repository at this point in the history
Allow registering additional headers for published entries
  • Loading branch information
tf authored Jun 12, 2024
2 parents 9c80702 + 5b09a97 commit cf5c162
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 10 deletions.
15 changes: 11 additions & 4 deletions app/controllers/pageflow/entries_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,22 +84,29 @@ def delegate_to_entry_type_frontend_app!(entry)
EntriesControllerEnvHelper.add_entry_info_to_env(request.env, entry: entry, mode: :published)

delegate_to_rack_app!(entry.entry_type.frontend_app) do |_status, headers, _body|
config = Pageflow.config_for(entry)

allow_iframe_for_embed(headers)
apply_cache_control(entry, headers)
apply_additional_headers(entry, config, headers)
apply_cache_control(entry, config, headers)
end
end

def allow_iframe_for_embed(headers)
headers.except!('X-Frame-Options') if params[:embed]
end

def apply_cache_control(entry, headers)
config = Pageflow.config_for(entry)

def apply_cache_control(entry, config, headers)
return if config.public_entry_cache_control_header.blank?
return if entry.password_protected?

headers['Cache-Control'] = config.public_entry_cache_control_header
end

def apply_additional_headers(entry, config, headers)
headers.merge!(
config.additional_public_entry_headers.for(entry, request)
)
end
end
end
8 changes: 4 additions & 4 deletions entry_types/scrolled/lib/pageflow_scrolled/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Configuration
# @example
#
# config.additional_frontend_packs.register(
# pageflow-scrolled/contentElements/some-pack',
# 'pageflow-scrolled/contentElements/some-pack',
# content_element_type_names: ['someType']
# )
#
Expand All @@ -23,7 +23,7 @@ class Configuration
# @example
#
# config.additional_editor_packs.register(
# pageflow-scrolled/contentElements/some-pack'
# 'pageflow-scrolled/contentElements/some-pack'
# )
#
# @return [AdditionalPacks]
Expand All @@ -36,8 +36,8 @@ class Configuration
# @example
#
# config.additional_frontend_seed_data.register(
# pageflow-scrolled/contentElements/some-pack',
# ->(entry, request) { {some: 'data'}}
# 'someData',
# ->(entry:, request:, **) { {some: 'data'}}
# )
#
# @return [AdditionalSeedData]
Expand Down
27 changes: 27 additions & 0 deletions lib/pageflow/additional_headers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module Pageflow
# Register additional response headers for published entries.
class AdditionalHeaders
# @api private
def initialize
@headers = []
end

# Either a hash of name values pair or a callable taking a
# {PublishedEntry} record and an {ActionDispatch::Request} object
# and returns a hash.
def register(headers)
@headers << headers
end

# @api private
def for(entry, request)
@headers.map { |headers|
if headers.respond_to?(:call)
headers.call(entry, request)
else
headers
end
}.reduce({}, :merge)
end
end
end
24 changes: 22 additions & 2 deletions lib/pageflow/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ class Configuration
attr_accessor :public_entry_request_scope

# Either a lambda or an object with a `call` method taking an
# {Entry} record and an {ActionDispatch::Request} object and
# returning `nil` or a path to redirect to. Can be used in
# {PublishedEntry} record and an {ActionDispatch::Request} object
# and returning `nil` or a path to redirect to. Can be used in
# conjuction with {PrimaryDomainEntryRedirect} to make sure
# entries are accessed via their account's configured cname.
#
Expand All @@ -208,6 +208,24 @@ class Configuration
# @return [String]
attr_accessor :public_entry_cache_control_header

# Provide additional response headers for published entries.
#
# @example
#
# config.additional_public_entry_headers.register(
# {'Some' => 'value'}
# )
#
# config.additional_public_entry_headers.register(
# lambda do |_entry, request|
# {'Some' => request.headers['Other']}
# end
# )
#
# @return [AdditionalHeaders]
# @since edge
attr_reader :additional_public_entry_headers

# Either a lambda or an object with a `call` method taking a
# {Site} as paramater and returing a hash of options used to
# construct the url of a published entry.
Expand Down Expand Up @@ -435,6 +453,7 @@ def initialize(target_type_name = nil)
@site_request_scope = CnameSiteRequestScope.new
@public_entry_request_scope = lambda { |entries, request| entries }
@public_entry_redirect = ->(_entry, _request) { nil }
@additional_public_entry_headers = AdditionalHeaders.new
@public_entry_url_options = Pageflow::SitesHelper::DEFAULT_PUBLIC_ENTRY_OPTIONS
@entry_embed_url_options = {protocol: 'https'}

Expand Down Expand Up @@ -578,6 +597,7 @@ def enable_all_features
delegate :themes, to: :config
delegate :widget_types, to: :config
delegate :public_entry_cache_control_header=, to: :config
delegate :additional_public_entry_headers, to: :config

delegate :for_entry_type, to: :config
end
Expand Down
32 changes: 32 additions & 0 deletions spec/pageflow/additional_headers_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
require 'spec_helper'

module Pageflow
describe AdditionalHeaders do
it 'returns empty hash by default' do
additional_headers = AdditionalHeaders.new
entry = create(:published_entry)

result = additional_headers.for(entry, request)

expect(result).to eq({})
end

it 'merges multiple registered hashes of headers' do
additional_headers = AdditionalHeaders.new
entry = create(:published_entry)

additional_headers.register('Some' => 'value')
additional_headers.register('Other' => 'header')
additional_headers.register(proc { {'Dynamic' => 'header'} })
result = additional_headers.for(entry, request)

expect(result).to eq('Some' => 'value',
'Other' => 'header',
'Dynamic' => 'header')
end

def request(uri = 'https://example.com')
ActionDispatch::Request.new(Rack::MockRequest.env_for(uri))
end
end
end
71 changes: 71 additions & 0 deletions spec/requests/pageflow/entries_show_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,77 @@ module Pageflow
expect(response.headers['Cache-Control']).to eq('public, max-age=3600')
end
end

describe 'with additional headers' do
it 'adds headers to responds' do
pageflow_configure do |config|
config.additional_public_entry_headers.register('X-Some' => 'value')
end

entry = create(:entry, :published,
type_name: 'test')

get(short_entry_url(entry))

expect(response.status).to eq(200)
expect(response.headers['X-Some']).to eq('value')
end

it 'passes entry and request to callable' do
pageflow_configure do |config|
config.additional_public_entry_headers.register(
lambda do |entry, request|
{
'X-From-Entry' => entry.type_name,
'X-From-Request' => request.subdomain
}
end
)
end

entry = create(:entry, :published,
type_name: 'test')

get(short_entry_url(entry), headers: {'HTTP_HOST' => 'news.example.com'})

expect(response.status).to eq(200)
expect(response.headers['X-From-Entry']).to eq('test')
expect(response.headers['X-From-Request']).to eq('news')
end

it 'does not use headers registered in feature flag by default' do
pageflow_configure do |config|
config.features.register('some_header') do |feature_config|
feature_config.additional_public_entry_headers.register('X-Some' => 'value')
end
end

entry = create(:entry, :published,
type_name: 'test')

get(short_entry_url(entry))

expect(response.status).to eq(200)
expect(response.headers).not_to have_key('X-Some')
end

it 'uses headers registered in enabled feature flag' do
pageflow_configure do |config|
config.features.register('some_header') do |feature_config|
feature_config.additional_public_entry_headers.register('X-Some' => 'value')
end
end

entry = create(:entry, :published,
type_name: 'test',
with_feature: 'some_header')

get(short_entry_url(entry))

expect(response.status).to eq(200)
expect(response.headers['X-Some']).to eq('value')
end
end
end
end
end

0 comments on commit cf5c162

Please sign in to comment.