Skip to content

Commit 724b3aa

Browse files
committed
Extracted nickname registration out of the UsersController and into its
own service.
1 parent c59c2a4 commit 724b3aa

3 files changed

Lines changed: 176 additions & 63 deletions

File tree

app/controllers/users_controller.rb

Lines changed: 3 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -119,77 +119,17 @@ def check_username
119119
# The special case where someone is changing the case of their own username
120120
return render_available_true if changing_case_of_own_username(target_user, username)
121121

122-
validator = UsernameValidator.new(username)
123-
if !validator.valid_format?
124-
render json: {errors: validator.errors}
125-
elsif !SiteSetting.call_discourse_hub?
126-
check_username_locally(username)
127-
else
128-
check_username_with_hub_server(target_user, username)
129-
end
122+
checker = UsernameCheckerService.new
123+
email = params[:email] || target_user.try(:email)
124+
render(json: checker.check_username(username, email))
130125
rescue RestClient::Forbidden
131126
render json: {errors: [I18n.t("discourse_hub.access_token_problem")]}
132127
end
133128

134-
def check_username_locally(username)
135-
if User.username_available?(username)
136-
render_available_true
137-
else
138-
render_unavailable_with_suggestion(UserNameSuggester.suggest(username))
139-
end
140-
end
141-
142129
def user_from_params_or_current_user
143130
params[:for_user_id] ? User.find(params[:for_user_id]) : current_user
144131
end
145132

146-
def available_globally_and_suggestion_from_hub(target_user, username, email_given)
147-
if email_given
148-
global_match, available, suggestion =
149-
DiscourseHub.nickname_match?(username, params[:email] || target_user.email)
150-
{ available_globally: available || global_match,
151-
suggestion_from_discourse_hub: suggestion,
152-
global_match: global_match }
153-
else
154-
args = DiscourseHub.nickname_available?(username)
155-
{ available_globally: args[0],
156-
suggestion_from_discourse_hub: args[1],
157-
global_match: false }
158-
end
159-
end
160-
161-
# Contact the Discourse Hub server
162-
def check_username_with_hub_server(target_user, username)
163-
email_given = (params[:email].present? || target_user.present?)
164-
available_locally = User.username_available?(username)
165-
info = available_globally_and_suggestion_from_hub(target_user, username, email_given)
166-
available_globally = info[:available_globally]
167-
suggestion_from_discourse_hub = info[:suggestion_from_discourse_hub]
168-
global_match = info[:global_match]
169-
if available_globally && available_locally
170-
render json: { available: true, global_match: (global_match ? true : false) }
171-
elsif available_locally && !available_globally
172-
if email_given
173-
# Nickname and email do not match what's registered on the discourse hub.
174-
render json: { available: false, global_match: false, suggestion: suggestion_from_discourse_hub }
175-
else
176-
# The nickname is available locally, but is registered on the discourse hub.
177-
# We need an email to see if the nickname belongs to this person.
178-
# Don't give a suggestion until we get the email and try to match it with on the discourse hub.
179-
render json: { available: false }
180-
end
181-
elsif available_globally && !available_locally
182-
# Already registered on this site with the matching nickname and email address. Why are you signing up again?
183-
render json: { available: false, suggestion: UserNameSuggester.suggest(username) }
184-
else
185-
# Not available anywhere.
186-
render_unavailable_with_suggestion(suggestion_from_discourse_hub)
187-
end
188-
end
189-
190-
def render_unavailable_with_suggestion(suggestion)
191-
render json: { available: false, suggestion: suggestion }
192-
end
193133

194134
def create
195135
return fake_success_response if suspicious? params
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
class UsernameCheckerService
2+
3+
def check_username(username, email)
4+
validator = UsernameValidator.new(username)
5+
if !validator.valid_format?
6+
{errors: validator.errors}
7+
elsif !SiteSetting.call_discourse_hub?
8+
check_username_locally(username)
9+
else
10+
check_username_with_hub_server(username, email)
11+
end
12+
13+
end
14+
15+
# Contact the Discourse Hub server
16+
def check_username_with_hub_server(username, email)
17+
available_locally = User.username_available?(username)
18+
info = available_globally_and_suggestion_from_hub(username, email)
19+
available_globally = info[:available_globally]
20+
suggestion_from_discourse_hub = info[:suggestion_from_discourse_hub]
21+
global_match = info[:global_match]
22+
if available_globally && available_locally
23+
{ available: true, global_match: (global_match ? true : false) }
24+
elsif available_locally && !available_globally
25+
if email.present?
26+
# Nickname and email do not match what's registered on the discourse hub.
27+
{ available: false, global_match: false, suggestion: suggestion_from_discourse_hub }
28+
else
29+
# The nickname is available locally, but is registered on the discourse hub.
30+
# We need an email to see if the nickname belongs to this person.
31+
# Don't give a suggestion until we get the email and try to match it with on the discourse hub.
32+
{ available: false }
33+
end
34+
elsif available_globally && !available_locally
35+
# Already registered on this site with the matching nickname and email address. Why are you signing up again?
36+
{ available: false, suggestion: UserNameSuggester.suggest(username) }
37+
else
38+
# Not available anywhere.
39+
render_unavailable_with_suggestion(suggestion_from_discourse_hub)
40+
end
41+
end
42+
43+
def render_unavailable_with_suggestion(suggestion)
44+
{ available: false, suggestion: suggestion }
45+
end
46+
47+
def check_username_locally(username)
48+
if User.username_available?(username)
49+
{ available: true }
50+
else
51+
{ available: false, suggestion: UserNameSuggester.suggest(username) }
52+
end
53+
end
54+
55+
def available_globally_and_suggestion_from_hub(username, email)
56+
if email.present?
57+
global_match, available, suggestion =
58+
DiscourseHub.nickname_match?(username, email)
59+
{ available_globally: available || global_match,
60+
suggestion_from_discourse_hub: suggestion,
61+
global_match: global_match }
62+
else
63+
args = DiscourseHub.nickname_available?(username)
64+
{ available_globally: args[0],
65+
suggestion_from_discourse_hub: args[1],
66+
global_match: false }
67+
end
68+
end
69+
end
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
require 'spec_helper'
2+
3+
describe UsernameCheckerService do
4+
5+
describe 'check_username' do
6+
7+
before do
8+
@service = UsernameCheckerService.new
9+
@nil_email = nil
10+
@email = 'vincentvega@example.com'
11+
end
12+
13+
context 'Username invalid' do
14+
it 'rejects blank usernames' do
15+
result = @service.check_username('', @nil_email)
16+
expect(result).to have_key(:errors)
17+
end
18+
it 'rejects too short usernames' do
19+
result = @service.check_username('a', @nil_email)
20+
expect(result).to have_key(:errors)
21+
end
22+
it 'rejects too long usernames' do
23+
result = @service.check_username('a123456789b123456789c123456789', @nil_email)
24+
expect(result).to have_key(:errors)
25+
end
26+
27+
it 'rejects usernames with invalid characters' do
28+
result = @service.check_username('vincent-', @nil_email)
29+
expect(result).to have_key(:errors)
30+
end
31+
32+
it 'rejects usernames that do not start with an alphanumeric character' do
33+
result = @service.check_username('_vincent', @nil_email)
34+
expect(result).to have_key(:errors)
35+
end
36+
end
37+
38+
context 'Using Discourse Hub' do
39+
before do
40+
SiteSetting.stubs(:call_discourse_hub?).returns(true)
41+
end
42+
43+
context 'and email is given' do
44+
it 'username is available locally but not globally' do
45+
DiscourseHub.stubs(:nickname_available?).returns([false, 'suggestion'])
46+
DiscourseHub.stubs(:nickname_match?).returns([true, false, nil])
47+
result = @service.check_username('vincent', @email)
48+
expected = { available: true, global_match: true }
49+
expect(result).to eq(expected)
50+
end
51+
52+
end
53+
54+
it 'username is available both locally and globally' do
55+
DiscourseHub.stubs(:nickname_available?).returns([true, nil])
56+
DiscourseHub.stubs(:nickname_match?).returns([false, true, nil])
57+
result = @service.check_username('vincent', @email)
58+
expected = { available: true, global_match: false }
59+
expect(result).to eq(expected)
60+
end
61+
62+
it 'username is available locally but not globally' do
63+
DiscourseHub.stubs(:nickname_match?).returns([false, true, nil])
64+
result = @service.check_username('vincent', @email)
65+
expected = { available: true, global_match: false }
66+
expect(result).to eq(expected)
67+
end
68+
69+
it 'username is available globally but not locally' do
70+
DiscourseHub.stubs(:nickname_match?).returns([false, true, nil])
71+
User.stubs(:username_available?).returns(false)
72+
UserNameSuggester.stubs(:suggest).returns('einar-j')
73+
expected = { available: false, suggestion: 'einar-j' }
74+
result = @service.check_username('vincent', @email)
75+
expect(result).to eq(expected)
76+
end
77+
78+
it 'username not available anywhere' do
79+
DiscourseHub.stubs(:nickname_match?).returns([false, false, 'suggestion'])
80+
expected = { available: false, suggestion: 'suggestion', global_match: false }
81+
@nil_email = nil
82+
result = @service.check_username('vincent', @email)
83+
expect(result).to eq(expected)
84+
end
85+
end
86+
87+
context 'Discourse Hub disabled' do
88+
it 'username not available locally' do
89+
User.stubs(:username_available?).returns(false)
90+
UserNameSuggester.stubs(:suggest).returns('einar-j')
91+
result = @service.check_username('vincent', @nil_email)
92+
result[:available].should be_false
93+
result[:suggestion].should eq('einar-j')
94+
end
95+
96+
it 'username available locally' do
97+
User.stubs(:username_available?).returns(true)
98+
result = @service.check_username('vincent', @nil_email)
99+
result[:available].should be_true
100+
end
101+
end
102+
end
103+
104+
end

0 commit comments

Comments
 (0)