monitor bsky user button
This commit is contained in:
@@ -3,7 +3,8 @@ class Domain::UsersController < DomainController
|
||||
extend T::Sig
|
||||
extend T::Helpers
|
||||
|
||||
before_action :set_user!, only: %i[show followed_by following]
|
||||
before_action :set_user!,
|
||||
only: %i[show followed_by following monitor_bluesky_user]
|
||||
before_action :set_post!, only: %i[users_faving_post]
|
||||
skip_before_action :authenticate_user!,
|
||||
only: %i[
|
||||
@@ -167,6 +168,23 @@ class Domain::UsersController < DomainController
|
||||
}
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def monitor_bluesky_user
|
||||
user = T.cast(@user, Domain::User::BlueskyUser)
|
||||
authorize user
|
||||
monitor = Domain::Bluesky::MonitoredObject.build_for_user(user)
|
||||
if monitor.save
|
||||
Domain::Bluesky::Job::ScanUserJob.perform_later(user:)
|
||||
Domain::Bluesky::Job::ScanPostsJob.perform_later(user:)
|
||||
flash[:notice] = "User is now being monitored"
|
||||
else
|
||||
flash[
|
||||
:alert
|
||||
] = "Error monitoring user: #{monitor.errors.full_messages.join(", ")}"
|
||||
end
|
||||
redirect_to domain_user_path(user)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
sig { override.returns(DomainController::DomainParamConfig) }
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
# typed: strict
|
||||
class Domain::User::BlueskyUserPolicy < Domain::UserPolicy
|
||||
sig { returns(T::Boolean) }
|
||||
def view_is_bluesky_user_monitored?
|
||||
is_role_admin? || is_role_moderator?
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def monitor_bluesky_user?
|
||||
is_role_admin?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,4 +3,22 @@
|
||||
<span class="font-medium italic text-slate-500">State</span>
|
||||
<span class=""><%= user.account_state_for_view %></span>
|
||||
</div>
|
||||
<% if policy(user).view_is_bluesky_user_monitored? %>
|
||||
<div class="flex flex-col">
|
||||
<span class="font-medium italic text-slate-500">Monitored</span>
|
||||
<% if Domain::Bluesky::MonitoredObject.exists?(kind: :user_did, value: user.did!) %>
|
||||
<span class="">Yes</span>
|
||||
<% else %>
|
||||
<% if policy(user).monitor_bluesky_user? %>
|
||||
<%= button_to(
|
||||
"Monitor",
|
||||
monitor_bluesky_user_domain_user_path(user),
|
||||
class: "inline-block text-blue-400 border border-blue-400 hover:bg-blue-600 px-1 hover:text-white rounded-md transition-colors duration-200"
|
||||
) %>
|
||||
<% else %>
|
||||
<span class="">No</span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -180,11 +180,11 @@
|
||||
<% end %>
|
||||
</header>
|
||||
<main class="flex flex-col grow bg-slate-200">
|
||||
<% if notice %>
|
||||
<p class="notice bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative" role="alert"><%= notice %></p>
|
||||
<% end %>
|
||||
<% if alert %>
|
||||
<p class="alert bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert"><%= alert %></p>
|
||||
<% if notice || alert %>
|
||||
<span class="max-w-2xl mx-auto mt-4 flex flex-col gap-2">
|
||||
<%= render "shared/flash_message", value: notice, class: "bg-green-100 border border-green-400 text-green-700" %>
|
||||
<%= render "shared/flash_message", value: alert, class: "bg-red-100 border border-red-400 text-red-700" %>
|
||||
</span>
|
||||
<% end %>
|
||||
<%= yield %>
|
||||
</main>
|
||||
|
||||
10
app/views/shared/_flash_message.html.erb
Normal file
10
app/views/shared/_flash_message.html.erb
Normal file
@@ -0,0 +1,10 @@
|
||||
<% if (value = local_assigns[:value]) && value.present? %>
|
||||
<div class="relative flex items-center justify-between gap-2 w-full <%= local_assigns[:class] %> px-4 py-3 rounded">
|
||||
<p role="alert"><%= value %></p>
|
||||
<button onclick="this.parentElement.remove()"
|
||||
type="button"
|
||||
class="opacity-50 hover:opacity-100 transition-opacity duration-200 border border-inherit p-1 leading-none w-6 h-6 rounded-sm flex items-center justify-center">
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
@@ -33,6 +33,7 @@ Rails.application.routes.draw do
|
||||
|
||||
get "followed_by", on: :member, action: :followed_by
|
||||
get "following", on: :member, action: :following
|
||||
post "monitor_bluesky_user", on: :member, action: :monitor_bluesky_user
|
||||
|
||||
resources :job_events, only: [], controller: "domain/user_job_events" do
|
||||
get "tracked_objects",
|
||||
|
||||
@@ -312,4 +312,112 @@ RSpec.describe Domain::UsersController, type: :controller do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST #monitor_bluesky_user" do
|
||||
let(:bluesky_user) { create(:domain_user_bluesky_user) }
|
||||
let(:composite_param) { "bsky@#{bluesky_user.handle}" }
|
||||
|
||||
before do
|
||||
# Sign in admin user for authentication
|
||||
sign_in admin_user
|
||||
|
||||
# Mock authorization to allow the action
|
||||
allow(controller).to receive(:authorize).and_return(true)
|
||||
|
||||
# Set @user instance variable as the controller does in find_user
|
||||
controller.instance_variable_set(:@user, bluesky_user)
|
||||
end
|
||||
|
||||
context "when monitoring succeeds" do
|
||||
it "creates a monitored object" do
|
||||
expect {
|
||||
post :monitor_bluesky_user, params: { id: composite_param }
|
||||
}.to change(Domain::Bluesky::MonitoredObject, :count).by(1)
|
||||
|
||||
monitor = Domain::Bluesky::MonitoredObject.last
|
||||
expect(monitor.value).to eq(bluesky_user.did)
|
||||
expect(monitor.kind).to eq("user_did")
|
||||
end
|
||||
|
||||
it "enqueues scan user job" do
|
||||
post :monitor_bluesky_user, params: { id: composite_param }
|
||||
|
||||
enqueued_jobs =
|
||||
SpecUtil.enqueued_job_args(Domain::Bluesky::Job::ScanUserJob)
|
||||
expect(enqueued_jobs).to contain_exactly(
|
||||
hash_including(user: bluesky_user),
|
||||
)
|
||||
end
|
||||
|
||||
it "enqueues scan posts job" do
|
||||
post :monitor_bluesky_user, params: { id: composite_param }
|
||||
|
||||
enqueued_jobs =
|
||||
SpecUtil.enqueued_job_args(Domain::Bluesky::Job::ScanPostsJob)
|
||||
expect(enqueued_jobs).to contain_exactly(
|
||||
hash_including(user: bluesky_user),
|
||||
)
|
||||
end
|
||||
|
||||
it "sets success flash message" do
|
||||
post :monitor_bluesky_user, params: { id: composite_param }
|
||||
expect(flash[:notice]).to eq("User is now being monitored")
|
||||
end
|
||||
|
||||
it "redirects to user page" do
|
||||
post :monitor_bluesky_user, params: { id: composite_param }
|
||||
expect(response).to redirect_to(domain_user_path(bluesky_user))
|
||||
end
|
||||
|
||||
it "authorizes the user" do
|
||||
expect(controller).to receive(:authorize).with(bluesky_user)
|
||||
post :monitor_bluesky_user, params: { id: composite_param }
|
||||
end
|
||||
end
|
||||
|
||||
context "when monitoring fails due to validation errors" do
|
||||
before do
|
||||
# Create existing monitor to trigger uniqueness validation failure
|
||||
Domain::Bluesky::MonitoredObject.create!(
|
||||
value: bluesky_user.did,
|
||||
kind: :user_did,
|
||||
)
|
||||
end
|
||||
|
||||
it "does not create a new monitored object" do
|
||||
expect {
|
||||
post :monitor_bluesky_user, params: { id: composite_param }
|
||||
}.not_to change(Domain::Bluesky::MonitoredObject, :count)
|
||||
end
|
||||
|
||||
it "fails to save the monitor due to uniqueness" do
|
||||
monitor = Domain::Bluesky::MonitoredObject.build_for_user(bluesky_user)
|
||||
expect(monitor.save).to be_falsey
|
||||
expect(monitor.errors[:value]).to include("has already been taken")
|
||||
end
|
||||
|
||||
it "does not enqueue any jobs" do
|
||||
SpecUtil.clear_enqueued_jobs!
|
||||
post :monitor_bluesky_user, params: { id: composite_param }
|
||||
expect(
|
||||
SpecUtil.enqueued_job_args(Domain::Bluesky::Job::ScanUserJob),
|
||||
).to be_empty
|
||||
expect(
|
||||
SpecUtil.enqueued_job_args(Domain::Bluesky::Job::ScanPostsJob),
|
||||
).to be_empty
|
||||
end
|
||||
|
||||
it "sets error flash message with validation errors" do
|
||||
post :monitor_bluesky_user, params: { id: composite_param }
|
||||
expect(flash[:alert]).to match(
|
||||
/Error monitoring user: .*has already been taken/,
|
||||
)
|
||||
end
|
||||
|
||||
it "redirects to user page" do
|
||||
post :monitor_bluesky_user, params: { id: composite_param }
|
||||
expect(response).to redirect_to(domain_user_path(bluesky_user))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user