Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(688) Add content block updates to a document's timeline #9754

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
34 changes: 33 additions & 1 deletion app/models/document/paginated_timeline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@ def entries
remarks = document.remarks_by_ids(query.remark_ids)
versions = document.decorated_edition_versions_by_ids(query.version_ids)

raw_entries.map do |entry|
versions_and_remarks = raw_entries.map do |entry|
case entry.model
when "Version"
versions.fetch(entry.id)
when "EditorialRemark"
remarks.fetch(entry.id)
end
end

if only.present?
versions_and_remarks
else
[*versions_and_remarks, *host_content_update_events].sort_by(&:created_at).reverse!
end
end
end

Expand Down Expand Up @@ -89,4 +95,30 @@ def prev_page
false
end
end

private

def next_page_entries
if next_page
Document::PaginatedTimelineQuery.new(document:, page: next_page, only:).raw_entries
end
end
Comment on lines +83 to +87
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move this below def date_window as it isn't called until further down in the file (nice to have methods defined in the order they're called)


def host_content_update_events
return [] if query.raw_entries.empty?

@host_content_update_events ||= HostContentUpdateEvent.all_for_date_window(
document:,
from: date_window.last,
to: date_window.first,
)
end

def date_window
@date_window ||= begin
start = page == 1 ? Time.zone.now : query.raw_entries.first.created_at
ends = next_page_entries ? next_page_entries.first.created_at : query.raw_entries.last.created_at
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The next_page logic is surprising - can you add to the commit detail what you've added to the PR description?

This is complicated slightly by the fact that the history is paginated. To compensate for this, we are having to "look ahead" to the next page to get the date of the next event to give us a window to query Publishing API for events.

Better yet, perhaps just handle the first page in this first commit, and then add the "next page" logic in a subsequent commit, rather than doing it all at once.

(start.to_time.round.utc..ends.to_time.round.utc)
end
end
end
1 change: 1 addition & 0 deletions features/support/publishing_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
GdsApi::PublishingApi.any_instance.stubs(:put_content)
GdsApi::PublishingApi.any_instance.stubs(:patch_links)
GdsApi::PublishingApi.any_instance.stubs(:unpublish)
GdsApi::PublishingApi.any_instance.stubs(:get_events_for_content_id).returns([])

need1 = {
"content_id" => SecureRandom.uuid,
Expand Down
12 changes: 12 additions & 0 deletions test/factories/host_content_update_events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FactoryBot.define do
factory :host_content_update_event do
author { create(:user) }
created_at { Time.zone.now }
content_id { SecureRandom.uuid }
content_title { "Some title" }

initialize_with do
new(author:, created_at:, content_id:, content_title:)
end
end
end
1 change: 1 addition & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ class ActiveSupport::TestCase
Sidekiq::Job.clear_all
stub_any_publishing_api_call
stub_publishing_api_publish_intent
Services.publishing_api.stubs(:get_events_for_content_id).returns([])
Services.stubs(:asset_manager).returns(stub_everything("asset-manager"))
end

Expand Down
157 changes: 135 additions & 22 deletions test/unit/app/models/document/paginated_timeline_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class PaginatedTimelineTest < ActiveSupport::TestCase
assert_equal entry_timestamps.sort.reverse, entry_timestamps
end

it "is is a list of VersionDecorator and EditorialRemark objects when no 'only' argument is passed in" do
it "is is a list of VersionDecorator and EditorialRemark objects when no 'only' argument is not passed in" do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is difficult to parse 😅 can we reword to remove the double negative?

Similarly on line 92

timeline = Document::PaginatedTimeline.new(document: @document, page: 1)
assert_equal [VersionDecorator, EditorialRemark].to_set,
timeline.entries.map(&:class).to_set
Expand Down Expand Up @@ -45,27 +45,6 @@ class PaginatedTimelineTest < ActiveSupport::TestCase
assert_equal unpaginated_results.each_slice(results_per_page).to_a, paginated_results
end

it "is a list of EditorialRemark objects when 'internal_notes' is passed into the 'only' argument" do
timeline = Document::PaginatedTimeline.new(document: @document, page: 1, only: "internal_notes")
expected_entries = [@editorial_remark3, @editorial_remark2, @editorial_remark1]

assert_equal expected_entries, timeline.entries
end

it "is a list of VersionDecorator objects when 'history' is passed in as a 'only' argument" do
timeline = Document::PaginatedTimeline.new(document: @document, page: 1, only: "history")
expected_actions = %w[updated
editioned
published
submitted
updated
rejected
submitted
created]

assert_equal expected_actions, timeline.entries.map(&:action)
end

it "correctly determines actions" do
mock_pagination(per_page: 30) do
timeline = Document::PaginatedTimeline.new(document: @document, page: 1)
Expand Down Expand Up @@ -96,6 +75,140 @@ class PaginatedTimelineTest < ActiveSupport::TestCase

assert_equal expected_actors, entries.map(&:actor)
end

describe "when there are HostContentUpdateEvents present" do
let(:host_content_update_events) { build_list(:host_content_update_event, 3) }

before do
HostContentUpdateEvent.stubs(:all_for_date_window).returns(host_content_update_events)
end

it "orders newest first (reverse chronological order)" do
timeline = Document::PaginatedTimeline.new(document: @document, page: 1)
entry_timestamps = timeline.entries.flatten.map(&:created_at)
assert_equal entry_timestamps.sort.reverse, entry_timestamps
end

it "is is a list of VersionDecorator, EditorialRemark and HostContentUpdateEvent objects when no 'only' argument is passed in" do
timeline = Document::PaginatedTimeline.new(document: @document, page: 1)
assert_equal [VersionDecorator, EditorialRemark, HostContentUpdateEvent].to_set,
timeline.entries.map(&:class).to_set
end

it "includes all of the HostContentUpdateEvent objects" do
timeline = Document::PaginatedTimeline.new(document: @document, page: 1)

host_content_update_events.each do |event|
assert_includes timeline.entries, event
end
end

describe "fetching HostContentUpdateEvents" do
before do
HostContentUpdateEvent.reset_mocha
end

describe "when on the first page" do
it "requests a date window from the first created_at date of the next page to the current datetime" do
Timecop.freeze do
page2_entries = Document::PaginatedTimeline.new(document: @document, page: 2).query.raw_entries

HostContentUpdateEvent.expects(:all_for_date_window).with(
document: @document,
from: page2_entries.first.created_at.to_time.utc,
to: Time.zone.now.to_time.round.utc,
).at_least_once.returns(host_content_update_events)

Document::PaginatedTimeline.new(document: @document, page: 1).entries
end
end

describe "when there are no results" do
it "does not request any events" do
timeline = Document::PaginatedTimeline.new(document: @document, page: 1)
timeline.query.expects(:raw_entries).at_least_once.returns([])

HostContentUpdateEvent.expects(:all_for_date_window).never

timeline.entries
end
end
end

describe "when not on the first page" do
it "requests a window between the first created_at date of the next page to the first created_at date of the current page" do
mock_pagination(per_page: 3) do
page2_entries = Document::PaginatedTimeline.new(document: @document, page: 2).query.raw_entries
page3_entries = Document::PaginatedTimeline.new(document: @document, page: 3).query.raw_entries

HostContentUpdateEvent.expects(:all_for_date_window).with(
document: @document,
from: page3_entries.first.created_at.to_time.utc,
to: page2_entries.first.created_at.to_time.utc,
).at_least_once.returns(host_content_update_events)

Document::PaginatedTimeline.new(document: @document, page: 2).entries
end
end
end

describe "when on the last page" do
it "fetches with the created_at date for the last item" do
Timecop.freeze do
mock_pagination(per_page: 30) do
timeline = Document::PaginatedTimeline.new(document: @document, page: 1)
timeline_entries = timeline.query.raw_entries

HostContentUpdateEvent.expects(:all_for_date_window).with(
document: @document,
from: timeline_entries.last.created_at.to_time.utc,
to: timeline_entries.first.created_at.to_time.utc,
).at_least_once.returns(host_content_update_events)

timeline.entries
end
end
end
end
end
end

describe "when only argument is set to history" do
it "is a list of VersionDecorator objects" do
timeline = Document::PaginatedTimeline.new(document: @document, page: 1, only: "history")
expected_actions = %w[updated
editioned
published
submitted
updated
rejected
submitted
created]

assert_equal expected_actions, timeline.entries.map(&:action)
end

it "does not fetch any HostContentUpdateEvents" do
HostContentUpdateEvent.expects(:all_for_date_window).never

Document::PaginatedTimeline.new(document: @document, page: 1, only: "history").entries
end
end

describe "when only argument is set to internal_notes" do
it "is a list of EditorialRemark objects when 'internal_notes'" do
timeline = Document::PaginatedTimeline.new(document: @document, page: 1, only: "internal_notes")
expected_entries = [@editorial_remark3, @editorial_remark2, @editorial_remark1]

assert_equal expected_entries, timeline.entries
end

it "does not fetch any HostContentUpdateEvents" do
HostContentUpdateEvent.expects(:all_for_date_window).never

Document::PaginatedTimeline.new(document: @document, page: 1, only: "internal_notes").entries
end
end
end

describe "#total_count" do
Expand Down