Skip to content

Commit

Permalink
Merge pull request #9812 from alphagov/content-modelling/774-user-page
Browse files Browse the repository at this point in the history
content modelling/774 user page
  • Loading branch information
Harriethw authored Jan 15, 2025
2 parents 518d9c3 + c68b35f commit e09b461
Show file tree
Hide file tree
Showing 22 changed files with 210 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,14 @@ def all_publishing_organisations
end

def updated_field_for(content_item)
user_copy = content_item.last_edited_by_editor ? mail_to(content_item.last_edited_by_editor.email, content_item.last_edited_by_editor.name, { class: "govuk-link" }) : "Unknown user"
user_copy = if content_item.last_edited_by_editor
link_to(
content_item.last_edited_by_editor.name,
helpers.content_block_manager.content_block_manager_user_path(content_item.last_edited_by_editor.uid), { class: "govuk-link" }
)
else
"Unknown user"
end
"#{time_ago_in_words(content_item.last_edited_at)} ago by #{user_copy}".html_safe
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<%= render "govuk_publishing_components/components/summary_list", {
items:,
} %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class ContentBlockManager::SignonUser::Show::SummaryListComponent < ViewComponent::Base
def initialize(user:)
@user = user
end

private

def items
[
name_item,
email_item,
organisation_item,
].compact
end

def name_item
{
field: "Name",
value: @user.name,
}
end

def email_item
{
field: "Email",
value: @user.email,
}
end

def organisation_item
{
field: "Organisation",
value: @user.organisation.name,
}
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class ContentBlockManager::UsersController < ContentBlockManager::BaseController
def show
@user = ContentBlockManager::SignonUser.with_uuids([params[:id]]).first

raise ActiveRecord::RecordNotFound, "Could not find User with ID #{params[:id]}" if @user.blank?
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ def for_document(content_block_document, page: nil, order: nil)
}.compact,
).parsed_content

editor_uuids = api_response["results"].map { |c| c["last_edited_by_editor_id"] }.compact
editors = editor_uuids.present? ? Editor.with_uuids(editor_uuids) : []
editor_uuids = api_response["results"].map { |c| c["last_edited_by_editor_id"] }.compact.uniq
editors = editor_uuids.present? ? ContentBlockManager::SignonUser.with_uuids(editor_uuids) : []

items = api_response["results"].map do |record|
from_api_record(record, editors)
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module ContentBlockManager
class SignonUser < Data.define(:uid, :name, :email, :organisation)
def self.with_uuids(uuids)
Services.signon_api_client.get_users(uuids:).map do |user|
new(
uid: user["uid"],
name: user["name"],
email: user["email"],
organisation: ContentBlockManager::SignonUser::Organisation.from_user_hash(user),
)
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module ContentBlockManager
class SignonUser
class Organisation < Data.define(:content_id, :name, :slug)
def self.from_user_hash(user)
if user["organisation"].present?
new(
content_id: user["organisation"]["content_id"],
name: user["organisation"]["name"],
slug: user["organisation"]["slug"],
)
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<% content_for :title, @user.name %>

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<%= render(
ContentBlockManager::SignonUser::Show::SummaryListComponent.new(
user: @user,
),
) %>
</div>
</div>
2 changes: 2 additions & 0 deletions lib/engines/content_block_manager/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
namespace :content_block_manager, path: "/" do
root to: "content_block/documents#index", via: :get

resources :users, only: %i[show]

namespace :content_block, path: "content-block" do
get "content-id/:content_id", to: "documents#content_id", as: :content_id
resources :documents, only: %i[index show new], path_names: { new: "(:block_type)/new" }, path: "" do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,13 +513,14 @@ def should_show_edit_form_for_email_address_content_block(document_title, email_
end

When(/^dependent content exists for a content block$/) do
host_editor_id = SecureRandom.uuid
@dependent_content = 10.times.map do |i|
{
"title" => "Content #{i}",
"document_type" => "document",
"base_path" => "/host-content-path-#{i}",
"content_id" => SecureRandom.uuid,
"last_edited_by_editor_id" => SecureRandom.uuid,
"last_edited_by_editor_id" => host_editor_id,
"last_edited_at" => 2.days.ago.to_s,
"host_content_id" => "abc12345",
"instances" => 1,
Expand All @@ -542,9 +543,11 @@ def should_show_edit_form_for_email_address_content_block(document_title, email_

stub_publishing_api_has_embedded_content_details(@dependent_content.first)

@host_content_editor = build(:signon_user, uid: host_editor_id)

stub_request(:get, "#{Plek.find('signon', external: true)}/api/users")
.with(query: { uuids: @dependent_content.map { |item| item["last_edited_by_editor_id"] } })
.to_return(body: [].to_json)
.with(query: { uuids: [host_editor_id] })
.to_return(body: [@host_content_editor].to_json)
end

Then(/^I should see the dependent content listed$/) do
Expand All @@ -554,6 +557,8 @@ def should_show_edit_form_for_email_address_content_block(document_title, email_
assert_text item["title"]
break if item == @dependent_content.last
end

expect(page).to have_link(@host_content_editor.name, href: content_block_manager.content_block_manager_user_path(@host_content_editor.uid))
end

Then(/^I (should )?see the rollup data for the dependent content$/) do |_should|
Expand Down Expand Up @@ -851,3 +856,28 @@ def click_save_and_continue
jobs = Sidekiq::ScheduledSet.new.select { |job| job.item["class"] == ContentBlockManager::SchedulePublishingWorker.to_s }
expect(jobs.count).to eq(0)
end

Given("A user exists with uuid {string}") do |uuid|
@user_from_signon = build(
:signon_user,
uid: uuid,
name: "John Doe",
email: "[email protected]",
organisation: build(:signon_user_organisation, content_id: "456", name: "User's Org", slug: "users-org"),
)

stub_request(:get, "#{Plek.find('signon', external: true)}/api/users")
.with(query: { uuids: [uuid] })
.to_return(body: [@user_from_signon].to_json)
end

When("I visit the user page for uuid {string}") do |uuid|
visit content_block_manager.content_block_manager_user_path(uuid)
end

Then("I should see the details for that user") do
expect(page).to have_selector("h1", text: @user_from_signon.name)
expect(page).to have_selector(".govuk-summary-list__value", text: @user_from_signon.name)
expect(page).to have_selector(".govuk-summary-list__value", text: @user_from_signon.email)
expect(page).to have_selector(".govuk-summary-list__value", text: @user_from_signon.organisation.name)
end
8 changes: 8 additions & 0 deletions lib/engines/content_block_manager/features/view_users.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Feature: View users
Background:
Given I am a GDS admin
And A user exists with uuid "123"

Scenario: GDS Editor views a user
When I visit the user page for uuid "123"
Then I should see the details for that user
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ContentBlockManager::ContentBlock::Document::Show::HostEditionsTableCompon
end
let(:unique_pageviews) { 1_200_000 }

let(:last_edited_by_editor) { build(:host_content_item_editor) }
let(:last_edited_by_editor) { build(:signon_user) }
let(:host_content_item) do
ContentBlockManager::HostContentItem.new(
"title" => "Some title",
Expand Down Expand Up @@ -88,7 +88,7 @@ def self.it_returns_unknown_user
assert_selector "tbody .govuk-table__cell", text: "1.2m"
assert_selector "tbody .govuk-table__cell", text: host_content_item.publishing_organisation["title"]
assert_selector "tbody .govuk-table__cell", text: "#{time_ago_in_words(host_content_item.last_edited_at)} ago by #{last_edited_by_editor.name}"
assert_link last_edited_by_editor.name, { href: "mailto:#{last_edited_by_editor.email}" }
assert_link last_edited_by_editor.name, { href: content_block_manager_user_path(last_edited_by_editor.uid) }
end

context "when the organisation does NOT exist within Whitehall" do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require "test_helper"

class ContentBlockManager::SignonUser::Show::SummaryListComponentTest < ViewComponent::TestCase
extend Minitest::Spec::DSL
include ContentBlockManager::Engine.routes.url_helpers

let(:organisation) { build(:signon_user_organisation, name: "Department for Example") }
let(:user) do
build(
:signon_user,
name: "John Smith",
email: "[email protected]",
organisation:,
)
end

it "renders a Govuk User correctly" do
render_inline(ContentBlockManager::SignonUser::Show::SummaryListComponent.new(user:))

assert_selector ".govuk-summary-list__row", count: 3

assert_selector ".govuk-summary-list__key", text: "Name"
assert_selector ".govuk-summary-list__value", text: user.name

assert_selector ".govuk-summary-list__key", text: "Email"
assert_selector ".govuk-summary-list__value", text: user.email

assert_selector ".govuk-summary-list__key", text: "Organisation"
assert_selector ".govuk-summary-list__value", text: user.organisation.name
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
document_type { "something" }
publishing_organisation { { name: "organisation", content_id: SecureRandom.uuid } }
publishing_app { "publishing_app" }
last_edited_by_editor { build(:host_content_item_editor) }
last_edited_by_editor { build(:signon_user) }
last_edited_at { 2.days.ago.to_s }
unique_pageviews { 123 }
instances { 1 }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
FactoryBot.define do
factory :host_content_item_editor, class: "ContentBlockManager::HostContentItem::Editor" do
factory :signon_user, class: "ContentBlockManager::SignonUser" do
uid { SecureRandom.uuid }
sequence(:name) { |i| "Someone #{i}" }
sequence(:email) { |i| "someone-#{i}@example.com" }
organisation { build(:host_content_item_editor_organisation) }
organisation { build(:signon_user_organisation) }

initialize_with do
new(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
FactoryBot.define do
factory :host_content_item_editor_organisation, class: "ContentBlockManager::HostContentItem::Editor::Organisation" do
factory :signon_user_organisation, class: "ContentBlockManager::SignonUser::Organisation" do
content_id { SecureRandom.uuid }
sequence(:name) { |i| "organisation #{i}" }
sequence(:slug) { |i| "organisation-#{i}" }
Expand Down
25 changes: 25 additions & 0 deletions lib/engines/content_block_manager/test/integration/users_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require "test_helper"
require "capybara/rails"

class ContentBlockManager::ContentBlock::UsersTest < ActionDispatch::IntegrationTest
include Capybara::DSL
extend Minitest::Spec::DSL
include ContentBlockManager::Engine.routes.url_helpers

setup do
logout
@organisation = create(:organisation)
user = create(:gds_admin, organisation: @organisation)
login_as(user)
end

describe "#show" do
let(:user_uuid) { SecureRandom.uuid }

it "returns 404 if the user doesn't exist" do
ContentBlockManager::SignonUser.expects(:with_uuids).with([user_uuid]).returns([])
visit content_block_manager_user_path(user_uuid)
assert_text "Could not find User with ID #{user_uuid}"
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def self.it_returns_embedded_content(&block)
end
end

let(:host_content_item_users) { build_list(:host_content_item_editor, 10) }
let(:host_content_item_users) { build_list(:signon_user, 10) }

before do
stub_publishing_api_has_embedded_content_for_any_content_id(
Expand All @@ -28,7 +28,7 @@ def self.it_returns_embedded_content(&block)
order: ContentBlockManager::HostContentItem::DEFAULT_ORDER,
)

ContentBlockManager::HostContentItem::Editor.stubs(:with_uuids).with(host_content_items.map { |i| i["last_edited_by_editor_id"] }).returns(host_content_item_users)
ContentBlockManager::SignonUser.stubs(:with_uuids).with(host_content_items.map { |i| i["last_edited_by_editor_id"] }).returns(host_content_item_users)
end

it "returns host content items" do
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class ContentBlockManager::HostContentItemTest < ActiveSupport::TestCase
}
end

let(:editor) { build(:host_content_item_editor, uid: last_edited_by_editor_id) }
let(:editor) { build(:signon_user, uid: last_edited_by_editor_id) }

let(:fake_api_response) do
GdsApi::Response.new(
Expand All @@ -53,7 +53,7 @@ class ContentBlockManager::HostContentItemTest < ActiveSupport::TestCase

before do
Services.expects(:publishing_api).returns(publishing_api_mock)
ContentBlockManager::HostContentItem::Editor.stubs(:with_uuids).returns([editor])
ContentBlockManager::SignonUser.stubs(:with_uuids).returns([editor])
end

it "calls the Publishing API for the content which embeds the target" do
Expand Down Expand Up @@ -82,7 +82,7 @@ class ContentBlockManager::HostContentItemTest < ActiveSupport::TestCase

it "calls the editor finder with the correct argument" do
publishing_api_mock.expects(:get_host_content_for_content_id).returns(fake_api_response)
ContentBlockManager::HostContentItem::Editor.expects(:with_uuids).with([last_edited_by_editor_id]).returns([editor])
ContentBlockManager::SignonUser.expects(:with_uuids).with([last_edited_by_editor_id]).returns([editor])

described_class.for_document(document)
end
Expand Down Expand Up @@ -125,7 +125,7 @@ class ContentBlockManager::HostContentItemTest < ActiveSupport::TestCase
it "returns nil for last_edited_by_editor" do
publishing_api_mock.expects(:get_host_content_for_content_id).returns(fake_api_response)

ContentBlockManager::HostContentItem::Editor.expects(:with_uuids).never
ContentBlockManager::SignonUser.expects(:with_uuids).never

result = described_class.for_document(document)

Expand Down
Loading

0 comments on commit e09b461

Please sign in to comment.