Skip to content

Commit

Permalink
Include JSONAPI params in etag
Browse files Browse the repository at this point in the history
  • Loading branch information
mamhoff committed May 13, 2024
1 parent 19f46bd commit 761dc59
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 5 deletions.
14 changes: 13 additions & 1 deletion app/controllers/alchemy/json_api/pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def index
@pages = filtered_pages.result
last_modified_page = @pages.select { |page| last_modified_for(page) }.max_by { |page| last_modified_for(page) }
last_modified_at = last_modified_for(last_modified_page) || Time.current
latest_etag = last_modified_page&.cache_key_with_version || Time.current.to_s
latest_etag = etag(last_modified_page)
if !@pages.all?(&:cache_page?)
render_pages_json(allowed) && return
elsif stale?(last_modified: last_modified_at, etag: latest_etag)
Expand Down Expand Up @@ -121,6 +121,18 @@ def api_page(page)
Alchemy::JsonApi::Page.new(page, page_version_type: page_version_type)
end

def etag(page)
return Time.current.to_s unless page
Digest::MD5.hexdigest(
page.cache_key_with_version
.concat(params[:include].to_s)
.concat(params[:fields].to_s)
.concat(params[:sort].to_s)
.concat(params[:filter].to_s)
.concat(params[:page].to_s)
)
end

def base_page_scope
# cancancan is not able to merge our complex AR scopes for logged in users
if can?(:edit_content, ::Alchemy::Page)
Expand Down
64 changes: 60 additions & 4 deletions spec/requests/alchemy/json_api/pages_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,27 @@
expect(response.headers["Cache-Control"]).to eq("max-age=10800, public, must-revalidate")
end

it "returns a different etag if different filters are present" do
get alchemy_json_api.page_path(page)
etag = response.headers["ETag"]
get alchemy_json_api.pages_path(filter: {page_layout_eq: "standard"})
expect(response.headers["ETag"]).not_to eq(etag)
end

it "returns a different etag if different include params are present" do
get alchemy_json_api.page_path(page)
etag = response.headers["ETag"]
get alchemy_json_api.pages_path(include: "all_elements.ingredients")
expect(response.headers["ETag"]).not_to eq(etag)
end

it "returns a different etag if different fields params are present" do
get alchemy_json_api.page_path(page)
etag = response.headers["ETag"]
get alchemy_json_api.pages_path(fields: "urlname")
expect(response.headers["ETag"]).not_to eq(etag)
end

context "if page is restricted" do
let(:page) do
FactoryBot.create(
Expand Down Expand Up @@ -182,9 +203,9 @@

describe "GET /alchemy/json_api/pages" do
context "with layoutpages and unpublished pages" do
let!(:layoutpage) { FactoryBot.create(:alchemy_page, :layoutpage, :public) }
let!(:non_public_page) { FactoryBot.create(:alchemy_page) }
let!(:public_page) { FactoryBot.create(:alchemy_page, :public, published_at: published_at) }
let!(:layoutpage) { FactoryBot.create(:alchemy_page, :layoutpage, :public, page_layout: "standard") }
let!(:non_public_page) { FactoryBot.create(:alchemy_page, page_layout: "standard") }
let!(:public_page) { FactoryBot.create(:alchemy_page, :public, published_at: published_at, page_layout: "standard") }

context "as anonymous user" do
let!(:pages) { [public_page] }
Expand All @@ -202,6 +223,41 @@
expect(response.headers["Cache-Control"]).to eq("max-age=10800, public, must-revalidate")
end

it "returns a different etag if different filters are present" do
get alchemy_json_api.pages_path
etag = response.headers["ETag"]
get alchemy_json_api.pages_path(filter: {page_layout_eq: "standard"})
expect(response.headers["ETag"]).not_to eq(etag)
end

it "returns a different etag if different sort params are present" do
get alchemy_json_api.pages_path
etag = response.headers["ETag"]
get alchemy_json_api.pages_path(sort: {id: :asc})
expect(response.headers["ETag"]).not_to eq(etag)
end

it "returns a different etag if different include params are present" do
get alchemy_json_api.pages_path
etag = response.headers["ETag"]
get alchemy_json_api.pages_path(include: "all_elements.ingredients")
expect(response.headers["ETag"]).not_to eq(etag)
end

it "returns a different etag if different fields params are present" do
get alchemy_json_api.pages_path
etag = response.headers["ETag"]
get alchemy_json_api.pages_path(fields: "urlname")
expect(response.headers["ETag"]).not_to eq(etag)
end

it "returns a different etag if different fields params are present" do
get alchemy_json_api.pages_path
etag = response.headers["ETag"]
get alchemy_json_api.pages_path(page: {number: 2, size: 1})
expect(response.headers["ETag"]).not_to eq(etag)
end

context "if one page is restricted" do
let!(:restricted_page) do
FactoryBot.create(
Expand Down Expand Up @@ -311,7 +367,7 @@
it "sets cache headers of latest matching page" do
get alchemy_json_api.pages_path(filter: {page_layout_eq: "news"})
expect(response.headers["Last-Modified"]).to eq(news_page2.published_at.utc.httpdate)
expect(response.headers["ETag"]).to eq('W/"e7a1c8beb22b58e94a605594d79766ad"')
expect(response.headers["ETag"]).to eq('W/"05cc385adeaf623ff5914f2a47c30072"')
expect(response.headers["Cache-Control"]).to eq("max-age=10800, public, must-revalidate")
end
end
Expand Down

0 comments on commit 761dc59

Please sign in to comment.