From bd1bd5f1992b8508e35a41493110ae85bf8543b8 Mon Sep 17 00:00:00 2001 From: Harriet H-W Date: Tue, 7 Jan 2025 11:33:19 +0000 Subject: [PATCH 1/4] delete task to migrate titles from CB Documents to Editions now that this has been run on all remote environments, it is no longer needed. --- ...ove_titles_from_documents_to_editions.rake | 32 --------- ..._titles_from_documents_to_editions_test.rb | 69 ------------------- 2 files changed, 101 deletions(-) delete mode 100644 lib/tasks/content_block_manager/move_titles_from_documents_to_editions.rake delete mode 100644 test/unit/lib/tasks/content_block_manager/move_titles_from_documents_to_editions_test.rb diff --git a/lib/tasks/content_block_manager/move_titles_from_documents_to_editions.rake b/lib/tasks/content_block_manager/move_titles_from_documents_to_editions.rake deleted file mode 100644 index 3d739a57a75..00000000000 --- a/lib/tasks/content_block_manager/move_titles_from_documents_to_editions.rake +++ /dev/null @@ -1,32 +0,0 @@ -desc "Moves the title from Content Block Documents to any Editions that do not have titles." -task :move_titles_from_documents_to_editions, %i[confirmation_string] => :environment do |_, args| - document_count = 0 - edition_count = 0 - confirmation_string = args[:confirmation_string] - is_real = confirmation_string == "run_for_real" - - ContentBlockManager::ContentBlock::Document.find_each do |document| - document_count += 1 - - document.editions.each do |edition| - document = edition.document - if edition.title.blank? - if is_real - edition.update!(title: document.title) - else - edition.assign_attributes(title: document.title) - end - edition_count += 1 - puts "Edition title set to #{edition.title} for Edition #{edition.id}" - else - puts "Skipping Edition #{edition.id} because title already set" - end - end - end - - if is_real - puts "Titles were changed from #{document_count} documents to #{edition_count} editions." - else - puts "This was a dry run. Titles would have been changed from #{document_count} documents to #{edition_count} editions." - end -end diff --git a/test/unit/lib/tasks/content_block_manager/move_titles_from_documents_to_editions_test.rb b/test/unit/lib/tasks/content_block_manager/move_titles_from_documents_to_editions_test.rb deleted file mode 100644 index b6a44e5bdff..00000000000 --- a/test/unit/lib/tasks/content_block_manager/move_titles_from_documents_to_editions_test.rb +++ /dev/null @@ -1,69 +0,0 @@ -require "test_helper" -require "rake" - -class MoveTitlesFromDocumentsToEditionsRake < ActiveSupport::TestCase - extend Minitest::Spec::DSL - - teardown { task.reenable } - - describe "#move_title_from_documents_to_editions" do - let(:task) { Rake::Task["move_titles_from_documents_to_editions"] } - let(:documents) { create_list(:content_block_document, 3, :email_address) } - let(:schemas) { build_list(:content_block_schema, 1, block_type: "email_address", body: { "properties" => {} }) } - - def log_output - log_output = "" - documents.each do |document| - log_output += document.editions.collect { |edition| - "Edition title set to #{edition.document.title} for Edition #{edition.id}" - }.join("\\n") - log_output += "\\n" - end - log_output - end - - before do - documents.each_with_index do |document, index| - document.update!(title: "document_title_#{index}") - create(:content_block_edition, document:) - create(:content_block_edition, document:) - end - - ContentBlockManager::ContentBlock::Schema.stubs(:all).returns(schemas) - end - - describe "default dry run feature" do - let(:dry_run_output) do - "This was a dry run. Titles would have been changed from 3 documents to 6 editions." - end - - it "logs an ouput for the title changes that would be made" do - assert_output(/#{log_output}#{dry_run_output}/) { task.invoke("dry_run") } - end - end - - describe "running for real" do - let(:confirmation_output) do - "Titles were changed from 3 documents to 6 editions." - end - - it "updates edition titles to their document titles" do - assert_output(/#{log_output}#{confirmation_output}/) { task.invoke("run_for_real") } - ContentBlockManager::ContentBlock::Edition.find_each do |edition| - assert_equal edition.document.title, edition.title - end - end - end - - describe "when title is already set" do - it "skips editions with titles" do - edition_with_title = create(:content_block_edition, document: documents.first) - edition_with_title.update!(title: "set title") - skipped_output = "Skipping Edition #{edition_with_title.id} because title already set" - - assert_output(/#{skipped_output}/) { task.invoke("run_for_real") } - assert_equal "set title", edition_with_title.title - end - end - end -end From f5d345f814c40b1da4271336deba27f0259d3b12 Mon Sep 17 00:00:00 2001 From: Harriet H-W Date: Tue, 7 Jan 2025 11:03:59 +0000 Subject: [PATCH 2/4] remove title from Content Block Document MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and use the title on Content Block Editions. In this big commit, we have to make a lot of changes which depend on each other: * Remove `title` data from Documents. This is slightly more complicated than just removing the column, because the `content_id_alias` was relying on the Document's title. To get around that, I've renamed the title column to `sluggable_string`, as Whitehall do for their Document. For the Content Block Manager, this field will only be assigned when the Document is first created, though it could be updated in the future when the title is changed. I don't think we are currently using the `content_id_alias` for CBM on the publishing side, but there may be work done to use it as a human-readable ID in the future. * assign the `sluggable_string`` whenever we create a new Document. * no longer update the Document `title` from the form data, instead update the Edition `title`. * Point Document’s title method to latest Edition - so we have a helper method to get the title straight from Doc. --- ...gable_string_on_content_block_documents.rb | 9 ++++ db/schema.rb | 4 +- .../show/confirm_summary_list_component.rb | 2 +- .../content_block_manager/base_controller.rb | 3 +- .../content_block/document.rb | 8 +++- .../document/scopes/searchable_by_keyword.rb | 2 +- .../content_block/edition.rb | 4 +- .../content_block/edition/documentable.rb | 8 +--- .../create_edition_service.rb | 7 +-- .../publish_edition_service.rb | 2 +- .../content_block/shared/_form.html.erb | 8 ++-- .../content_block_manager_steps.rb | 13 +++-- .../index/summary_card_component_test.rb | 4 +- .../confirm_summary_list_component_test.rb | 9 ++-- .../test/factories/content_block_document.rb | 2 +- .../test/factories/content_block_edition.rb | 2 + .../content_block/documents_test.rb | 4 +- .../content_block/editions_test.rb | 8 ++-- .../content_block/workflow_test.rb | 8 ++-- .../app/models/content_block_document_test.rb | 24 +++++++--- .../scopes/searchable_by_keyword_test.rb | 47 +++++++++++-------- .../app/models/content_block_edition_test.rb | 20 +++----- .../services/create_edition_service_test.rb | 8 ++-- .../services/publish_edition_service_test.rb | 7 +-- 24 files changed, 122 insertions(+), 91 deletions(-) create mode 100644 db/migrate/20250106160308_change_title_to_sluggable_string_on_content_block_documents.rb diff --git a/db/migrate/20250106160308_change_title_to_sluggable_string_on_content_block_documents.rb b/db/migrate/20250106160308_change_title_to_sluggable_string_on_content_block_documents.rb new file mode 100644 index 00000000000..7f134f1b524 --- /dev/null +++ b/db/migrate/20250106160308_change_title_to_sluggable_string_on_content_block_documents.rb @@ -0,0 +1,9 @@ +class ChangeTitleToSluggableStringOnContentBlockDocuments < ActiveRecord::Migration[7.1] + def up + rename_column :content_block_documents, :title, :sluggable_string + end + + def down + rename_column :content_block_documents, :sluggable_string, :title + end +end diff --git a/db/schema.rb b/db/schema.rb index e228b5da240..4b09da4056b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2025_01_06_111536) do +ActiveRecord::Schema[7.1].define(version: 2025_01_06_160308) do create_table "assets", charset: "utf8mb3", force: :cascade do |t| t.string "asset_manager_id", null: false t.string "variant", null: false @@ -193,7 +193,7 @@ create_table "content_block_documents", charset: "utf8mb3", force: :cascade do |t| t.string "content_id" - t.string "title" + t.string "sluggable_string" t.string "block_type" t.datetime "created_at", precision: nil t.datetime "updated_at", precision: nil diff --git a/lib/engines/content_block_manager/app/components/content_block_manager/content_block_edition/show/confirm_summary_list_component.rb b/lib/engines/content_block_manager/app/components/content_block_manager/content_block_edition/show/confirm_summary_list_component.rb index 486e7974232..b9810a90162 100644 --- a/lib/engines/content_block_manager/app/components/content_block_manager/content_block_edition/show/confirm_summary_list_component.rb +++ b/lib/engines/content_block_manager/app/components/content_block_manager/content_block_edition/show/confirm_summary_list_component.rb @@ -28,7 +28,7 @@ def edit_item def title_item { field: "Title", - value: content_block_edition.document_title, + value: content_block_edition.title, } end diff --git a/lib/engines/content_block_manager/app/controllers/content_block_manager/base_controller.rb b/lib/engines/content_block_manager/app/controllers/content_block_manager/base_controller.rb index f6a53e1d5fd..b25d06acfc4 100644 --- a/lib/engines/content_block_manager/app/controllers/content_block_manager/base_controller.rb +++ b/lib/engines/content_block_manager/app/controllers/content_block_manager/base_controller.rb @@ -25,7 +25,8 @@ def edition_params "scheduled_publication(3i)", "scheduled_publication(4i)", "scheduled_publication(5i)", - document_attributes: %w[title block_type], + :title, + document_attributes: %w[block_type], details: @schema.fields, ) .merge!(creator: current_user) diff --git a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/document.rb b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/document.rb index 6de27759b9c..7123a1bcf93 100644 --- a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/document.rb +++ b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/document.rb @@ -6,7 +6,7 @@ class Document < ApplicationRecord include Scopes::SearchableByUpdatedDate extend FriendlyId - friendly_id :title, use: :slugged, slug_column: :content_id_alias, routes: :default + friendly_id :sluggable_string, use: :slugged, slug_column: :content_id_alias, routes: :default has_many :editions, -> { order(created_at: :asc, id: :asc) }, @@ -15,7 +15,7 @@ class Document < ApplicationRecord enum :block_type, ContentBlockManager::ContentBlock::Schema.valid_schemas.index_with(&:to_s) attr_readonly :block_type - validates :block_type, :title, presence: true + validates :block_type, :sluggable_string, presence: true has_one :latest_edition, -> { joins(:document).where("content_block_documents.latest_edition_id = content_block_editions.id") }, @@ -29,6 +29,10 @@ class Document < ApplicationRecord def embed_code "{{embed:content_block_#{block_type}:#{content_id}}}" end + + def title + @title ||= latest_edition&.title + end end end end diff --git a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/document/scopes/searchable_by_keyword.rb b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/document/scopes/searchable_by_keyword.rb index 726100bf7ca..aaebb4a2d04 100644 --- a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/document/scopes/searchable_by_keyword.rb +++ b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/document/scopes/searchable_by_keyword.rb @@ -3,7 +3,7 @@ module ContentBlock::Document::Scopes::SearchableByKeyword extend ActiveSupport::Concern SQL = <<-SQL.freeze - content_block_documents.title REGEXP :pattern OR#{' '} + content_block_editions.title REGEXP :pattern OR#{' '} content_block_editions.details REGEXP :pattern OR#{' '} content_block_editions.instructions_to_publishers REGEXP :pattern SQL diff --git a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition.rb b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition.rb index b30d966e13a..1346e20472a 100644 --- a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition.rb +++ b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition.rb @@ -8,6 +8,8 @@ class Edition < ApplicationRecord include ValidatesDetails include Workflow + validates :title, presence: true + scope :current_versions, lambda { joins( "LEFT JOIN content_block_documents document ON document.latest_edition_id = content_block_editions.id", @@ -22,7 +24,7 @@ def render ContentBlockTools::ContentBlock.new( document_type: "content_block_#{block_type}", content_id: document.content_id, - title: document_title, + title:, details:, ).render end diff --git a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition/documentable.rb b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition/documentable.rb index 9e7ce001591..3e6fb23860e 100644 --- a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition/documentable.rb +++ b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition/documentable.rb @@ -11,10 +11,6 @@ module ContentBlock::Edition::Documentable accepts_nested_attributes_for :document end - def document_title - document&.title || @document_title - end - def block_type document&.block_type || @block_type end @@ -24,12 +20,12 @@ def ensure_presence_of_document self.document = ContentBlock::Document.new( content_id: create_random_id, block_type: @block_type, - title: @document_title, + sluggable_string: title, ) elsif document.new_record? document.content_id = create_random_id if document.content_id.blank? document.block_type = @block_type if document.block_type.blank? - document.title = @title if document.title.blank? + document.sluggable_string = title if document.sluggable_string.blank? end end diff --git a/lib/engines/content_block_manager/app/services/content_block_manager/create_edition_service.rb b/lib/engines/content_block_manager/app/services/content_block_manager/create_edition_service.rb index 4028e1dc5ea..36278047bdb 100644 --- a/lib/engines/content_block_manager/app/services/content_block_manager/create_edition_service.rb +++ b/lib/engines/content_block_manager/app/services/content_block_manager/create_edition_service.rb @@ -5,7 +5,6 @@ def initialize(schema) end def call(edition_params, document_id: nil) - edition_params[:title] = edition_params[:document_attributes][:title] @new_edition = build_edition(edition_params, document_id) @new_edition.assign_attributes(edition_params) @new_edition.save! @@ -18,10 +17,12 @@ def build_edition(edition_params, document_id) if document_id.nil? ContentBlockManager::ContentBlock::Edition.new(edition_params) else - # TODO: is this covered when adding title? ContentBlockManager::ContentBlock::Edition.new( document_id:, - document_attributes: edition_params.delete(:document_attributes).except(:block_type).merge({ id: document_id }), + title: edition_params[:title], + document_attributes: edition_params.delete(:document_attributes) + .except(:block_type) + .merge({ id: document_id }), ) end end diff --git a/lib/engines/content_block_manager/app/services/content_block_manager/publish_edition_service.rb b/lib/engines/content_block_manager/app/services/content_block_manager/publish_edition_service.rb index 83e0400633e..af91ead3b13 100644 --- a/lib/engines/content_block_manager/app/services/content_block_manager/publish_edition_service.rb +++ b/lib/engines/content_block_manager/app/services/content_block_manager/publish_edition_service.rb @@ -20,7 +20,7 @@ def publish_with_rollback(content_block_edition) content_id:, content_id_alias:, schema_id: schema.id, - title: content_block_edition.document_title, + title: content_block_edition.title, details: content_block_edition.details, instructions_to_publishers: content_block_edition.instructions_to_publishers, links: { diff --git a/lib/engines/content_block_manager/app/views/content_block_manager/content_block/shared/_form.html.erb b/lib/engines/content_block_manager/app/views/content_block_manager/content_block/shared/_form.html.erb index 9830b6fa263..4aaedf4052b 100644 --- a/lib/engines/content_block_manager/app/views/content_block_manager/content_block/shared/_form.html.erb +++ b/lib/engines/content_block_manager/app/views/content_block_manager/content_block/shared/_form.html.erb @@ -12,10 +12,10 @@ label: { text: "Title", }, - name: "content_block/edition[document_attributes][title]", - id: "content_block_manager/content_block/edition_document_title", - value: @form.content_block_edition.document&.title, - error_items: errors_for(@form.content_block_edition.errors, "document.title".to_sym), + name: "content_block/edition[title]", + id: "content_block_manager/content_block/edition_title", + value: @form.content_block_edition&.title, + error_items: errors_for(@form.content_block_edition.errors, "edition.title".to_sym), } %> <% @form.attributes.each do |field, _value| %> diff --git a/lib/engines/content_block_manager/features/step_definitions/content_block_manager_steps.rb b/lib/engines/content_block_manager/features/step_definitions/content_block_manager_steps.rb index 2e1d5ffda0d..33209e71f4d 100644 --- a/lib/engines/content_block_manager/features/step_definitions/content_block_manager_steps.rb +++ b/lib/engines/content_block_manager/features/step_definitions/content_block_manager_steps.rb @@ -143,7 +143,6 @@ assert_not_nil edition assert_not_nil edition.document - assert_equal @title, edition.document_title if @title.present? assert_equal @title, edition.title if @title.present? assert_equal @instructions_to_publishers, edition.instructions_to_publishers if @instructions_to_publishers.present? @@ -233,6 +232,7 @@ def has_support_button details: { email_address: @email_address }, creator: @user, organisation:, + title: "previously created title", ) ContentBlockManager::ContentBlock::Edition::HasAuditTrail.acting_as(@user) do @content_block.publish! @@ -248,7 +248,7 @@ def has_support_button instructions_to_publishers = fields.delete("instructions_to_publishers") (1..count.to_i).each do |_i| - document = create(:content_block_document, block_type.to_sym, title:) + document = create(:content_block_document, block_type.to_sym, sluggable_string: title.parameterize(separator: "_")) editions = create_list( :content_block_edition, @@ -259,6 +259,7 @@ def has_support_button details: fields, creator: @user, instructions_to_publishers:, + title:, ) document.latest_edition = editions.last @@ -271,7 +272,8 @@ def has_support_button @content_blocks ||= [] @email_address = "foo@example.com" organisation = create(:organisation) - document = create(:content_block_document, :email_address, title: fields[:title]) + title = fields.delete("title") || "title" + document = create(:content_block_document, :email_address, sluggable_string: title.parameterize(separator: "_")) @content_block = create( :content_block_edition, :email_address, @@ -279,6 +281,7 @@ def has_support_button details: { email_address: fields[:email_address] }, creator: @user, organisation:, + title:, ) ContentBlockManager::ContentBlock::Edition::HasAuditTrail.acting_as(@user) do @content_block.publish! @@ -811,8 +814,8 @@ def click_save_and_continue within(".govuk-summary-card", text: content_block_name) do find("a", text: "Copy code").click has_text?("Code copied") - document = ContentBlockManager::ContentBlock::Document.find_by(title: content_block_name) - @embed_code = document.embed_code + edition = ContentBlockManager::ContentBlock::Edition.find_by(title: content_block_name) + @embed_code = edition.document.embed_code end end diff --git a/lib/engines/content_block_manager/test/components/content_block/document/index/summary_card_component_test.rb b/lib/engines/content_block_manager/test/components/content_block/document/index/summary_card_component_test.rb index b5488f87e37..b5053aa3f7e 100644 --- a/lib/engines/content_block_manager/test/components/content_block/document/index/summary_card_component_test.rb +++ b/lib/engines/content_block_manager/test/components/content_block/document/index/summary_card_component_test.rb @@ -24,7 +24,7 @@ class ContentBlockManager::ContentBlock::Document::Index::SummaryCardComponentTe it "renders a published content block as a summary card" do render_inline(ContentBlockManager::ContentBlock::Document::Index::SummaryCardComponent.new(content_block_document:)) - assert_selector ".govuk-summary-card__title", text: content_block_edition.document_title + assert_selector ".govuk-summary-card__title", text: content_block_edition.title assert_selector ".govuk-summary-card__action", count: 1 assert_selector ".govuk-summary-card__action .govuk-link[href='#{content_block_manager_content_block_document_path(content_block_document)}']" @@ -33,7 +33,7 @@ class ContentBlockManager::ContentBlock::Document::Index::SummaryCardComponentTe assert_selector ".govuk-summary-list__row", count: 6 assert_selector ".govuk-summary-list__key", text: "Title" - assert_selector ".govuk-summary-list__value", text: content_block_edition.document_title + assert_selector ".govuk-summary-list__value", text: content_block_edition.title assert_selector ".govuk-summary-list__key", text: "Foo" assert_selector ".govuk-summary-list__value", text: "bar" diff --git a/lib/engines/content_block_manager/test/components/content_block/edition/show/confirm_summary_list_component_test.rb b/lib/engines/content_block_manager/test/components/content_block/edition/show/confirm_summary_list_component_test.rb index 33f4892e9dd..72f83fbbd98 100644 --- a/lib/engines/content_block_manager/test/components/content_block/edition/show/confirm_summary_list_component_test.rb +++ b/lib/engines/content_block_manager/test/components/content_block/edition/show/confirm_summary_list_component_test.rb @@ -20,11 +20,12 @@ class ContentBlockManager::ContentBlockEdition::Show::ConfirmSummaryListComponen it "renders a summary list component with the edition details to confirm" do organisation = create(:organisation, name: "Department for Example") - content_block_document = create(:content_block_document, :email_address, title: "Some title") + content_block_document = create(:content_block_document, :email_address) content_block_edition = create( :content_block_edition, :email_address, + title: "Some edition title", details: { "interesting_fact" => "value of fact" }, organisation:, document: content_block_document, @@ -37,7 +38,7 @@ class ContentBlockManager::ContentBlockEdition::Show::ConfirmSummaryListComponen assert_selector ".govuk-summary-list__key", text: "Email address details" assert_selector ".govuk-summary-list__actions", text: "Edit" assert_selector ".govuk-summary-list__key", text: "Title" - assert_selector ".govuk-summary-list__value", text: "Some title" + assert_selector ".govuk-summary-list__value", text: "Some edition title" assert_selector ".govuk-summary-list__key", text: "New interesting fact" assert_selector ".govuk-summary-list__value", text: "value of fact" assert_selector ".govuk-summary-list__key", text: "Lead organisation" @@ -50,7 +51,7 @@ class ContentBlockManager::ContentBlockEdition::Show::ConfirmSummaryListComponen it "shows the scheduled date time" do organisation = create(:organisation, name: "Department for Example") - content_block_document = create(:content_block_document, :email_address, title: "Some title") + content_block_document = create(:content_block_document, :email_address) content_block_edition = create( :content_block_edition, @@ -76,7 +77,7 @@ class ContentBlockManager::ContentBlockEdition::Show::ConfirmSummaryListComponen it "shows a publish now row" do organisation = create(:organisation, name: "Department for Example") - content_block_document = create(:content_block_document, :email_address, title: "Some title") + content_block_document = create(:content_block_document, :email_address) _previous_edition = create( :content_block_edition, diff --git a/lib/engines/content_block_manager/test/factories/content_block_document.rb b/lib/engines/content_block_manager/test/factories/content_block_document.rb index def828691b1..f5bc3365689 100644 --- a/lib/engines/content_block_manager/test/factories/content_block_document.rb +++ b/lib/engines/content_block_manager/test/factories/content_block_document.rb @@ -1,7 +1,7 @@ FactoryBot.define do factory :content_block_document, class: "ContentBlockManager::ContentBlock::Document" do sequence(:content_id) { SecureRandom.uuid } - title { "Title" } + sluggable_string { "factory-example-title" } created_at { Time.zone.now.utc } updated_at { Time.zone.now.utc } latest_edition_id { nil } diff --git a/lib/engines/content_block_manager/test/factories/content_block_edition.rb b/lib/engines/content_block_manager/test/factories/content_block_edition.rb index c75680bf64e..77bca2df351 100644 --- a/lib/engines/content_block_manager/test/factories/content_block_edition.rb +++ b/lib/engines/content_block_manager/test/factories/content_block_edition.rb @@ -14,6 +14,8 @@ instructions_to_publishers { nil } + title { "Factory Title for Edition" } + ContentBlockManager::ContentBlock::Schema.valid_schemas.each do |type| trait type.to_sym do document { build(:content_block_document, block_type: type) } diff --git a/lib/engines/content_block_manager/test/integration/content_block/documents_test.rb b/lib/engines/content_block_manager/test/integration/content_block/documents_test.rb index 108b5d02f57..60de65ff494 100644 --- a/lib/engines/content_block_manager/test/integration/content_block/documents_test.rb +++ b/lib/engines/content_block_manager/test/integration/content_block/documents_test.rb @@ -45,12 +45,12 @@ class ContentBlockManager::ContentBlock::DocumentsTest < ActionDispatch::Integra details: { "email_address" => "live_edition@example.com" }, document_id: document_with_latest_edition.id, ) - document_without_latest_edition = create(:content_block_document, :email_address, title: "no latest edition") + _document_without_latest_edition = create(:content_block_document, :email_address, sluggable_string: "no latest edition") visit content_block_manager.content_block_manager_content_block_documents_path({ lead_organisation: "" }) assert_text document_with_latest_edition.latest_edition.details["email_address"] - assert_no_text document_without_latest_edition.title + assert_text "1 result" end describe "when no filter params are specified" do diff --git a/lib/engines/content_block_manager/test/integration/content_block/editions_test.rb b/lib/engines/content_block_manager/test/integration/content_block/editions_test.rb index 53a5fe88f35..074cfa6dac0 100644 --- a/lib/engines/content_block_manager/test/integration/content_block/editions_test.rb +++ b/lib/engines/content_block_manager/test/integration/content_block/editions_test.rb @@ -53,9 +53,9 @@ class ContentBlockManager::ContentBlock::EditionsTest < ActionDispatch::Integrat let(:content_block_document) { create(:content_block_document, :email_address) } let!(:original_edition) { create(:content_block_edition, :email_address, document: content_block_document) } + let(:title) { "Some Title" } let(:document_attributes) do { - title: "Some Title", block_type: "email_address", }.with_indifferent_access end @@ -81,6 +81,7 @@ class ContentBlockManager::ContentBlock::EditionsTest < ActionDispatch::Integrat document_attributes:, details:, organisation_id: organisation.id, + title:, }, } end @@ -92,8 +93,8 @@ class ContentBlockManager::ContentBlock::EditionsTest < ActionDispatch::Integrat new_version = ContentBlockManager::ContentBlock::Version.last new_edition_organisation = ContentBlockManager::ContentBlock::EditionOrganisation.last - assert_equal document_attributes[:title], content_block_document.title assert_equal document_attributes[:block_type], content_block_document.block_type + assert_equal title, new_edition.title assert_equal details, new_edition.details assert_equal new_edition.document_id, content_block_document.id @@ -137,6 +138,7 @@ class ContentBlockManager::ContentBlock::EditionsTest < ActionDispatch::Integrat "content_block/edition": { document_attributes:, details:, + title:, organisation_id: organisation.id, }, } @@ -147,7 +149,7 @@ class ContentBlockManager::ContentBlock::EditionsTest < ActionDispatch::Integrat version = ContentBlockManager::ContentBlock::Version.last edition_organisation = ContentBlockManager::ContentBlock::EditionOrganisation.last - assert_equal document_attributes[:title], document.title + assert_equal title, edition.title assert_equal document_attributes[:block_type], document.block_type assert_equal details, edition.details diff --git a/lib/engines/content_block_manager/test/integration/content_block/workflow_test.rb b/lib/engines/content_block_manager/test/integration/content_block/workflow_test.rb index d379c4a8d4d..d0a2e5af45c 100644 --- a/lib/engines/content_block_manager/test/integration/content_block/workflow_test.rb +++ b/lib/engines/content_block_manager/test/integration/content_block/workflow_test.rb @@ -16,8 +16,8 @@ class ContentBlockManager::ContentBlock::WorkflowTest < ActionDispatch::Integrat end let(:organisation) { create(:organisation) } - let(:document) { create(:content_block_document, :email_address, content_id: @content_id, title: "Some Title") } - let(:edition) { create(:content_block_edition, document:, details:, organisation:, instructions_to_publishers: "instructions") } + let(:document) { create(:content_block_document, :email_address, content_id: @content_id, sluggable_string: "some-slug") } + let(:edition) { create(:content_block_edition, document:, details:, organisation:, instructions_to_publishers: "instructions", title: "Some Edition Title") } setup do login_as_admin @@ -162,8 +162,8 @@ def assert_edition_is_published(&block) schema_name: "content_block_type", document_type: "content_block_type", publishing_app: "whitehall", - title: "Some Title", - content_id_alias: "some-title", + title: "Some Edition Title", + content_id_alias: "some-slug", instructions_to_publishers: "instructions", details: { "foo" => "Foo text", diff --git a/lib/engines/content_block_manager/test/unit/app/models/content_block_document_test.rb b/lib/engines/content_block_manager/test/unit/app/models/content_block_document_test.rb index 1d2f2e266a7..00b89cf036b 100644 --- a/lib/engines/content_block_manager/test/unit/app/models/content_block_document_test.rb +++ b/lib/engines/content_block_manager/test/unit/app/models/content_block_document_test.rb @@ -8,13 +8,13 @@ class ContentBlockManager::ContentBlockDocumentTest < ActiveSupport::TestCase :content_block_document, :email_address, content_id: "52084b2d-4a52-4e69-ba91-3052b07c7eb6", - title: "Title", + sluggable_string: "Title", created_at: Time.zone.local(2000, 12, 31, 23, 59, 59).utc, updated_at: Time.zone.local(2000, 12, 31, 23, 59, 59).utc, ) assert_equal "52084b2d-4a52-4e69-ba91-3052b07c7eb6", content_block_document.content_id - assert_equal "Title", content_block_document.title + assert_equal "Title", content_block_document.sluggable_string assert_equal "email_address", content_block_document.block_type assert_equal Time.zone.local(2000, 12, 31, 23, 59, 59).utc, content_block_document.created_at assert_equal Time.zone.local(2000, 12, 31, 23, 59, 59).utc, content_block_document.updated_at @@ -89,7 +89,7 @@ class ContentBlockManager::ContentBlockDocumentTest < ActiveSupport::TestCase content_block_document = create( :content_block_document, :email_address, - title: "This is a title", + sluggable_string: "This is a title", ) assert_equal "this-is-a-title", content_block_document.content_id_alias @@ -100,24 +100,34 @@ class ContentBlockManager::ContentBlockDocumentTest < ActiveSupport::TestCase :content_block_document, 2, :email_address, - title: "This is a title", + sluggable_string: "This is a title", ) assert_equal "this-is-a-title", content_block_documents[0].content_id_alias assert_equal "this-is-a-title--2", content_block_documents[1].content_id_alias end - it "does not change the alias if the title changes" do + it "does not change the alias if the sluggable string changes" do content_block_document = create( :content_block_document, :email_address, - title: "This is a title", + sluggable_string: "This is a title", ) - content_block_document.title = "Something else" + content_block_document.sluggable_string = "Something else" content_block_document.save! assert_equal "this-is-a-title", content_block_document.content_id_alias end end + + describe "title" do + it "returns the latest edition's title" do + document = create(:content_block_document, :email_address) + _oldest_edition = create(:content_block_edition, document:) + latest_edition = create(:content_block_edition, document:, title: "I am the latest edition") + + assert_equal latest_edition.title, document.title + end + end end diff --git a/lib/engines/content_block_manager/test/unit/app/models/content_block_edition/document/scopes/searchable_by_keyword_test.rb b/lib/engines/content_block_manager/test/unit/app/models/content_block_edition/document/scopes/searchable_by_keyword_test.rb index 9dad14ee9ec..ffb6aad1e30 100644 --- a/lib/engines/content_block_manager/test/unit/app/models/content_block_edition/document/scopes/searchable_by_keyword_test.rb +++ b/lib/engines/content_block_manager/test/unit/app/models/content_block_edition/document/scopes/searchable_by_keyword_test.rb @@ -5,59 +5,68 @@ class ContentBlockManager::SearchableByKeywordTest < ActiveSupport::TestCase describe ".with_keyword" do test "should find documents with title containing keyword" do - document_with_first_keyword = create(:content_block_document, :email_address, title: "klingons and such") + document_with_first_keyword = create(:content_block_document, :email_address) _edition_with_first_keyword = create(:content_block_edition, :email_address, document: document_with_first_keyword, - details: { "email_address" => "hello@hello.com" }) - document_without_first_keyword = create(:content_block_document, :email_address, title: "this document is about muppets") - _edition_without_first_keyword = create(:content_block_edition, :email_address, document: document_without_first_keyword) + details: { "email_address" => "hello@hello.com" }, + title: "klingons and such") + document_without_first_keyword = create(:content_block_document, :email_address) + _edition_without_first_keyword = create(:content_block_edition, :email_address, document: document_without_first_keyword, + title: "this document is about muppets") assert_equal [document_with_first_keyword], ContentBlockManager::ContentBlock::Document.with_keyword("klingons") end test "should find documents with title containing keywords not in order" do - document_with_first_keyword = create(:content_block_document, :email_address, title: "klingons and such") + document_with_first_keyword = create(:content_block_document, :email_address) _edition_with_first_keyword = create(:content_block_edition, :email_address, document: document_with_first_keyword, - details: { "email_address" => "hello@hello.com" }) - _document_without_first_keyword = create(:content_block_document, :email_address, title: "muppets and such") + details: { "email_address" => "hello@hello.com" }, + title: "klingons and such") + _document_without_first_keyword = create(:content_block_document, :email_address) assert_equal [document_with_first_keyword], ContentBlockManager::ContentBlock::Document.with_keyword("such klingons") end test "should find documents with latest edition's details containing keyword" do - document_with_first_keyword = create(:content_block_document, :email_address, title: "example title") + document_with_first_keyword = create(:content_block_document, :email_address) _edition_with_first_keyword = create(:content_block_edition, document: document_with_first_keyword, - details: { "foo" => "Foo text", "bar" => "Bar text" }) - document_without_first_keyword = create(:content_block_document, :email_address, title: "this document is about muppets") + details: { "foo" => "Foo text", "bar" => "Bar text" }, + title: "example title") + document_without_first_keyword = create(:content_block_document, :email_address) _edition_without_first_keyword = create(:content_block_edition, document: document_without_first_keyword, - details: { "something" => "something" }) + details: { "something" => "something" }, + title: "this document is about muppets") assert_equal [document_with_first_keyword], ContentBlockManager::ContentBlock::Document.with_keyword("foo bar") end test "should find documents with instructions to publishers containing keyword" do - document_with_first_keyword = create(:content_block_document, :email_address, title: "example title") + document_with_first_keyword = create(:content_block_document, :email_address) _edition_with_first_keyword = create(:content_block_edition, document: document_with_first_keyword, - instructions_to_publishers: "foo") - document_without_first_keyword = create(:content_block_document, :email_address, title: "this document is about muppets") + instructions_to_publishers: "foo", + title: "example title") + document_without_first_keyword = create(:content_block_document, :email_address) _edition_without_first_keyword = create(:content_block_edition, document: document_without_first_keyword, - instructions_to_publishers: "bar") + instructions_to_publishers: "bar", + title: "this document is about muppets") assert_equal [document_with_first_keyword], ContentBlockManager::ContentBlock::Document.with_keyword("foo") end test "should find documents with details or title containing keyword" do - document_with_keyword_in_details = create(:content_block_document, :email_address, title: "example title") + document_with_keyword_in_details = create(:content_block_document, :email_address) _edition_with_keyword = create(:content_block_edition, document: document_with_keyword_in_details, - details: { "foo" => "Foo text", "bar" => "Bar text" }) - document_with_keyword_in_title = create(:content_block_document, :email_address, title: "this document is about bar foo") + details: { "foo" => "Foo text", "bar" => "Bar text" }, + title: "example title") + document_with_keyword_in_title = create(:content_block_document, :email_address) _edition_without_keyword = create(:content_block_edition, document: document_with_keyword_in_title, - details: { "something" => "something" }) + details: { "something" => "something" }, + title: "this document is about bar foo") assert_equal [document_with_keyword_in_details, document_with_keyword_in_title], ContentBlockManager::ContentBlock::Document.with_keyword("foo bar") end end diff --git a/lib/engines/content_block_manager/test/unit/app/models/content_block_edition_test.rb b/lib/engines/content_block_manager/test/unit/app/models/content_block_edition_test.rb index 395cff66a2e..c0180aa386c 100644 --- a/lib/engines/content_block_manager/test/unit/app/models/content_block_edition_test.rb +++ b/lib/engines/content_block_manager/test/unit/app/models/content_block_edition_test.rb @@ -8,7 +8,7 @@ class ContentBlockManager::ContentBlockEditionTest < ActiveSupport::TestCase let(:created_at) { Time.zone.local(2000, 12, 31, 23, 59, 59).utc } let(:updated_at) { Time.zone.local(2000, 12, 31, 23, 59, 59).utc } let(:details) { { "some_field" => "some_content" } } - let(:title) { "Document title" } + let(:title) { "Edition title" } let(:creator) { create(:user) } let(:organisation) { create(:organisation) } @@ -19,10 +19,10 @@ class ContentBlockManager::ContentBlockEditionTest < ActiveSupport::TestCase details:, document_attributes: { block_type: "email_address", - title:, }, creator:, organisation_id: organisation.id.to_s, + title:, ) end @@ -38,6 +38,7 @@ class ContentBlockManager::ContentBlockEditionTest < ActiveSupport::TestCase assert_equal created_at, content_block_edition.created_at assert_equal updated_at, content_block_edition.updated_at assert_equal details, content_block_edition.details + assert_equal title, content_block_edition.title end it "persists the block type to the document" do @@ -48,14 +49,6 @@ class ContentBlockManager::ContentBlockEditionTest < ActiveSupport::TestCase assert_equal document.block_type, content_block_edition.block_type end - it "persists the title to the document" do - content_block_edition.save! - content_block_edition.reload - document = content_block_edition.document - - assert_equal document.title, content_block_edition.document_title - end - it "creates a document" do content_block_edition.save! content_block_edition.reload @@ -89,16 +82,15 @@ class ContentBlockManager::ContentBlockEditionTest < ActiveSupport::TestCase assert content_block_edition.errors.full_messages.include?("Document block type cannot be blank") end - it "validates the presence of a document title" do + it "validates the presence of an edition title" do content_block_edition = build( :content_block_edition, created_at:, updated_at:, details:, - document_attributes: { - title: nil, - }, + document_attributes: {}, organisation_id: organisation.id.to_s, + title: nil, ) assert_invalid content_block_edition diff --git a/lib/engines/content_block_manager/test/unit/app/services/create_edition_service_test.rb b/lib/engines/content_block_manager/test/unit/app/services/create_edition_service_test.rb index ebce323a59f..6bfaf869b9f 100644 --- a/lib/engines/content_block_manager/test/unit/app/services/create_edition_service_test.rb +++ b/lib/engines/content_block_manager/test/unit/app/services/create_edition_service_test.rb @@ -12,7 +12,6 @@ class ContentBlockManager::CreateEditionServiceTest < ActiveSupport::TestCase let(:edition_params) do { document_attributes: { - title: new_title, block_type: "email_address", }.with_indifferent_access, details: { @@ -21,6 +20,7 @@ class ContentBlockManager::CreateEditionServiceTest < ActiveSupport::TestCase }, creator: build(:user), organisation_id: organisation.id.to_s, + title: new_title, } end @@ -49,9 +49,8 @@ class ContentBlockManager::CreateEditionServiceTest < ActiveSupport::TestCase new_document = ContentBlockManager::ContentBlock::Document.find_by!(content_id:) new_edition = new_document.editions.first - assert_equal new_title, new_document.title - assert_equal new_title, new_edition.title assert_equal edition_params[:document_attributes][:block_type], new_document.block_type + assert_equal new_title, new_edition.title assert_equal edition_params[:details], new_edition.details assert_equal new_edition.document_id, new_document.id assert_equal new_edition.lead_organisation.id, organisation.id @@ -70,9 +69,8 @@ class ContentBlockManager::CreateEditionServiceTest < ActiveSupport::TestCase new_edition = document.editions.last - assert_equal new_title, document.title - assert_equal new_title, new_edition.title assert_equal edition_params[:details], new_edition.details + assert_equal edition_params[:title], new_edition.title assert_equal new_edition.document_id, document.id assert_equal new_edition.lead_organisation.id, organisation.id end diff --git a/lib/engines/content_block_manager/test/unit/app/services/publish_edition_service_test.rb b/lib/engines/content_block_manager/test/unit/app/services/publish_edition_service_test.rb index df0f407ec35..fe0536e9d47 100644 --- a/lib/engines/content_block_manager/test/unit/app/services/publish_edition_service_test.rb +++ b/lib/engines/content_block_manager/test/unit/app/services/publish_edition_service_test.rb @@ -6,7 +6,7 @@ class ContentBlockManager::PublishEditionServiceTest < ActiveSupport::TestCase describe "#call" do let(:content_id) { "49453854-d8fd-41da-ad4c-f99dbac601c3" } let(:schema) { build(:content_block_schema, block_type: "content_block_type", body: { "properties" => { "foo" => "", "bar" => "" } }) } - let(:document) { create(:content_block_document, :email_address, content_id:, title: "Some Title") } + let(:document) { create(:content_block_document, :email_address, content_id:, sluggable_string: "some-edition-title") } let(:edition) do create( :content_block_edition, @@ -14,6 +14,7 @@ class ContentBlockManager::PublishEditionServiceTest < ActiveSupport::TestCase details: { "foo" => "Foo text", "bar" => "Bar text" }, organisation: @organisation, instructions_to_publishers: "instructions", + title: "Some Edition Title", ) end @@ -59,8 +60,8 @@ class ContentBlockManager::PublishEditionServiceTest < ActiveSupport::TestCase schema_name: schema.id, document_type: schema.id, publishing_app: "whitehall", - title: "Some Title", - content_id_alias: "some-title", + title: "Some Edition Title", + content_id_alias: "some-edition-title", instructions_to_publishers: "instructions", details: { "foo" => "Foo text", From e052614b44539e4eed4f42a2c6f612895241f05e Mon Sep 17 00:00:00 2001 From: Harriet H-W Date: Tue, 7 Jan 2025 11:36:37 +0000 Subject: [PATCH 3/4] update content block editions model diagram to reflect that title is now on Edition --- docs/diagrams/content_block_manager_models.puml | 2 +- docs/diagrams/object_store_models.png | Bin 0 -> 36339 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 docs/diagrams/object_store_models.png diff --git a/docs/diagrams/content_block_manager_models.puml b/docs/diagrams/content_block_manager_models.puml index db98eda3b48..b6a5b598bfb 100644 --- a/docs/diagrams/content_block_manager_models.puml +++ b/docs/diagrams/content_block_manager_models.puml @@ -6,7 +6,6 @@ together { class ContentBlockDocument { content_id block_type - title } database content_block_documents ContentBlockDocument .> content_block_documents @@ -17,6 +16,7 @@ together { details document_id state + title } database content_block_editions ContentBlockEdition .> content_block_editions diff --git a/docs/diagrams/object_store_models.png b/docs/diagrams/object_store_models.png new file mode 100644 index 0000000000000000000000000000000000000000..156d5b226dda724f512d3c4b962f479d7b35fcea GIT binary patch literal 36339 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 R{fK`bJ)|U;bnxu;{{yM(Cx`$5 literal 0 HcmV?d00001 From 4d79134a7275569764104165b7913e0874f7799e Mon Sep 17 00:00:00 2001 From: Harriet H-W Date: Tue, 7 Jan 2025 16:42:46 +0000 Subject: [PATCH 4/4] refactor Content Block Document validation because we have these lines ``` belongs_to :document, touch: true ... accepts_nested_attributes_for :document ``` the `document` should never be nil when creating an Edition. --- .../content_block/edition/documentable.rb | 11 ++--------- .../app/validators/organisation_validator_test.rb | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition/documentable.rb b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition/documentable.rb index 3e6fb23860e..c34506714f7 100644 --- a/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition/documentable.rb +++ b/lib/engines/content_block_manager/app/models/content_block_manager/content_block/edition/documentable.rb @@ -12,19 +12,12 @@ module ContentBlock::Edition::Documentable end def block_type - document&.block_type || @block_type + @block_type ||= document&.block_type end def ensure_presence_of_document - if document.blank? - self.document = ContentBlock::Document.new( - content_id: create_random_id, - block_type: @block_type, - sluggable_string: title, - ) - elsif document.new_record? + if document.new_record? document.content_id = create_random_id if document.content_id.blank? - document.block_type = @block_type if document.block_type.blank? document.sluggable_string = title if document.sluggable_string.blank? end end diff --git a/lib/engines/content_block_manager/test/unit/app/validators/organisation_validator_test.rb b/lib/engines/content_block_manager/test/unit/app/validators/organisation_validator_test.rb index 5c779cddf7b..7877c0b6fad 100644 --- a/lib/engines/content_block_manager/test/unit/app/validators/organisation_validator_test.rb +++ b/lib/engines/content_block_manager/test/unit/app/validators/organisation_validator_test.rb @@ -4,7 +4,7 @@ class ContentBlockManager::OrganisationValidatorTest < ActiveSupport::TestCase extend Minitest::Spec::DSL test "it validates presence of a lead organisation" do - content_block_edition = build(:content_block_edition, organisation: nil) + content_block_edition = build(:content_block_edition, organisation: nil, document: build(:content_block_document, :email_address)) assert_equal false, content_block_edition.valid? assert_equal ["cannot be blank"], content_block_edition.errors.messages[:lead_organisation]