FEATURE: Add scheduled Twitter login problem check - Part 1 (#25830)

This PR adds a new scheduled problem check that simply tries to connect to Twitter OAuth endpoint to check that it's working. It is using the default retry strategy of 2 retries 30 seconds apart.
This commit is contained in:
Ted Johansson 2024-02-26 12:08:12 +08:00 committed by GitHub
parent a1d7548869
commit ed2496c59d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 149 additions and 2 deletions

View File

@ -1506,6 +1506,7 @@ en:
description: "Top 10 users who have had likes from a wide range of people."
dashboard:
twitter_login_warning: 'Twitter login appears to not be working at the moment. Check the credentials in <a href="%{base_path}/admin/site_settings/category/login?filter=twitter">the Site Settings</a>.'
group_email_credentials_warning: 'There was an issue with the email credentials for the group <a href="%{base_path}/g/%{group_name}/manage/email">%{group_full_name}</a>. No emails will be sent from the group inbox until this problem is addressed. %{error}'
rails_env_warning: "Your server is running in %{env} mode."
host_names_warning: "Your config/database.yml file is using the default localhost hostname. Update it to use your site's hostname."

View File

@ -9,6 +9,16 @@ class Auth::TwitterAuthenticator < Auth::ManagedAuthenticator
SiteSetting.enable_twitter_logins
end
def healthy?
connection =
Faraday.new(url: "https://api.twitter.com") do |config|
config.basic_auth(SiteSetting.twitter_consumer_key, SiteSetting.twitter_consumer_secret)
end
connection.post("/oauth2/token").status == 200
rescue Faraday::Error
false
end
def after_authenticate(auth_token, existing_account: nil)
# Twitter sends a huge amount of data which we don't need, so ignore it
auth_token[:extra] = {}

View File

@ -3,6 +3,8 @@
class ProblemCheck
include ActiveSupport::Configurable
config_accessor :priority, default: "low", instance_writer: false
# Determines if the check should be performed at a regular interval, and if
# so how often. If left blank, the check will be performed every time the
# admin dashboard is loaded, or the data is otherwise requested.
@ -50,4 +52,25 @@ class ProblemCheck
def call
raise NotImplementedError
end
private
def problem
[
Problem.new(
I18n.t(translation_key, base_path: Discourse.base_path),
priority: self.config.priority,
identifier:,
),
]
end
def no_problem
[]
end
def translation_key
# TODO: Infer a default based on class name, then move translations in locale file.
raise NotImplementedError
end
end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
class ProblemCheck::TwitterLogin < ProblemCheck
self.priority = "high"
# TODO: Implement.
self.perform_every = 24.hours
def call
return no_problem if !authenticator.enabled?
return no_problem if authenticator.healthy?
problem
end
private
def authenticator
@authenticator ||= Auth::TwitterAuthenticator.new
end
def translation_key
"dashboard.twitter_login_warning"
end
end

View File

@ -34,4 +34,13 @@ RSpec.describe Jobs::ProblemChecks do
},
) { described_class.new.execute([]) }
end
it "does not schedule non-scheduled checks" do
expect_not_enqueued_with(
job: :problem_check,
args: {
check_identifier: "non_scheduled_check",
},
) { described_class.new.execute([]) }
end
end

View File

@ -65,4 +65,37 @@ RSpec.describe Auth::TwitterAuthenticator do
expect(authenticator.description_for_user(user)).to eq("")
end
end
describe "#healthy?" do
let(:authenticator) { described_class.new }
let(:connection) { mock("Faraday::Connection") }
let(:response) { mock("Faraday::Response") }
before do
Faraday.stubs(:new).returns(connection)
connection.stubs(:post).returns(response)
response.stubs(:status).returns(status)
end
context "when endpoint is reachable" do
let(:status) { 200 }
it { expect(authenticator).to be_healthy }
end
context "when credentials aren't recognized" do
let(:status) { 403 }
it { expect(authenticator).not_to be_healthy }
end
context "when an unexpected error happens" do
let(:status) { anything }
before { connection.stubs(:post).raises(Faraday::ServerError) }
it { expect(authenticator).not_to be_healthy }
end
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
RSpec.describe ProblemCheck::TwitterLogin do
let(:problem_check) { described_class.new }
let(:authenticator) { mock("Auth::TwitterAuthenticator") }
before { Auth::TwitterAuthenticator.stubs(:new).returns(authenticator) }
describe "#call" do
context "when Twitter authentication isn't enabled" do
before { authenticator.stubs(:enabled?).returns(false) }
it { expect(problem_check.call).to be_empty }
end
context "when Twitter authentication appears to work" do
before do
authenticator.stubs(:enabled?).returns(true)
authenticator.stubs(:healthy?).returns(true)
end
it { expect(problem_check.call).to be_empty }
end
context "when Twitter authentication appears not to work" do
before do
authenticator.stubs(:enabled?).returns(true)
authenticator.stubs(:healthy?).returns(false)
Discourse.stubs(:base_path).returns("foo.bar")
end
it do
expect(problem_check.call).to contain_exactly(
have_attributes(
identifier: :twitter_login,
priority: "high",
message:
'Twitter login appears to not be working at the moment. Check the credentials in <a href="foo.bar/admin/site_settings/category/login?filter=twitter">the Site Settings</a>.',
),
)
end
end
end
end

View File

@ -26,11 +26,12 @@ RSpec.describe ProblemCheck do
end
describe ".checks" do
it { expect(described_class.checks).to contain_exactly(scheduled_check, unscheduled_check) }
it { expect(described_class.checks).to include(scheduled_check, unscheduled_check) }
end
describe ".scheduled" do
it { expect(described_class.scheduled).to contain_exactly(scheduled_check) }
it { expect(described_class.scheduled).to include(scheduled_check) }
it { expect(described_class.scheduled).not_to include(unscheduled_check) }
end
describe ".scheduled?" do