-
Notifications
You must be signed in to change notification settings - Fork 515
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AO3-3997 Fix for email address validation #4390 #4565
Conversation
|
||
it "raises an exception when the external author email is invalid" do | ||
expect do | ||
@sp.parse_author("", "Author Name", "not_email") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RSpec/InstanceVariable: Avoid instance variables – use let, a method call, or a local variable (if possible).
app/models/story_parser.rb
Outdated
|
||
# if the name and email don't exist in the DB tables, add it | ||
unless name.blank? | ||
external_author_name = ExternalAuthorName.where(name: name, external_author_id: external_author.id).first || | ||
raise Error, external_author.errors.full_messages.join(" ") if external_author.invalid? | ||
|
||
external_author_name = ExternalAuthorName.find_by(name: name, external_author_id: external_author.id) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this comment look accurate? I didn't understand the intention of this part of the code first, and the comment I inserted was my best guess.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Poking @ariana-paris who knows the story parser best
app/models/story_parser.rb
Outdated
unless name.blank? | ||
external_author_name = ExternalAuthorName.where(name: name, external_author_id: external_author.id).first || | ||
raise Error, external_author.errors.full_messages.join(" ") if external_author.invalid? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this line is in the wrong place. If someone enters !!!!!
as the name, and also uses an invalid email address, then the exclamation points in the name will be stripped down so that name
is blank. So even though it's an invalid email, I don't think it will reach this line and show the error. I'm pretty sure this line needs to be outside of the unless name.blank?
block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your comment. In that scenario (and for the scenario where the name is blank and the email is invalid), should the error just say "invalid email"? Or should it alert about the name and the email?
EDIT: Actually, it looks the name with invalid characters only (like "!!!!") is not supposed to cause an error, or at least that's the status quo. So my comment above is more for the blank name and invalid email case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be more difficult to handle that case, since the ExternalAuthor
currently doesn't get created if either the name
or the email
is blank, so it never runs through the validator. Since it's not producing a 500 error, I'd be tempted to let it slide for the moment, but someone who's more familiar with the Open Doors committee might have a better answer for you.
(Really, I think the entire function should be rewritten, so that there are proper error messages for everything instead of silently fixing invalid names, and it's easier to follow the code. But IDK if that's in scope, and IDK if that's what Open Doors wants.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also reorganized parser_author_common
in a way that makes sense to me. And to avoid breaking something, I added tests and confirmed the behavior didn't change (as far as I can see) except for the email address validation, too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're going to rewrite it for clarity reasons, I think it'd be cleaner to consolidate most of the name-related code. As is, redacted_name
is set, and then there are a bunch of lines involving errors & emails, and then redacted_name
is finally used. But there's no reason for it to be set that early, and it's easier to keep track of the variable's purpose if it's set right before it's used.
Similarly, the two different possible values of external_author_name
are set in two different if
blocks, so it's hard to see at a glance what the function is supposed to return. It'd be cleaner to set it equal to external_author.default_name
in the else
of the if redacted_name.present?
block.
With some other cleanup, that could get you something like this:
def parse_author_common(email, name)
errors = []
errors << "No author name specified" if name.blank?
if email.present?
external_author = ExternalAuthor.find_or_create_by(email: email)
errors += external_author.errors.full_messages
else
errors << "No author email specified"
end
raise Error, errors.join("\n") if errors.present?
# convert to ASCII and strip out invalid characters (everything except alphanumeric characters, _, @ and -)
redacted_name = name.to_ascii.gsub(/[^\w[ \-@.]]/u, "")
if redacted_name.present?
external_author.names.find_or_create_by(name: redacted_name)
else
external_author.default_name
end
end
Ideally, it'd also be nice to move some or all of the error-checking code here into the models, but that's almost certainly out of scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for the rewrite. Do you think .save
is unneccesary? I thought the method makes persistent changes to the databases so that author names can be retrieved by querying emails later. (Or maybe it's implicitly saved somehow?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
find_or_create_by
calls create
under the hood, and create
calls save
under the hood. So both the external author and the external author name are getting saved to the database if they don't already exist, because they're both fetched/created with find_or_create_by
in the modified code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. Thanks for explaining.
@@ -10,8 +10,9 @@ | |||
<dd><%= invitation.invitee_email %></dd> | |||
<% else %> | |||
<dd> | |||
<%= error_messages_for @invitation %> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I poked around a bit to see where we usually put this, and it looks like most of the time it's either after the page heading (which we don't have here) or the first or second thing inside the form. Can we move this inside the form_for
?
You might also be able to use invitation
rather than @invitation
here.
<%= form_for(invitation) do |f| %> | ||
<p><label for="invitee_email">Enter an email address:</label> <%= f.text_field :invitee_email %></p> | ||
<p><%= label_tag "invitation[invitee_email]", t('.email_address_label') %>: <%= f.text_field :invitee_email %></p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
f.label :invitee_email
should work here, and it would be more consistent since we're using f.text_field :invitee_email
. If you could also use double quotes around the translation key name and remove the colon (we've been slowly eliminating them on our form labels, plus taking it out of the actual label means it doesn't get the red highlight when there's an error), that would be great!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, that's fine!
And an admin grants the request | ||
And I try to invite a friend from my user page | ||
When all emails have been delivered | ||
And I fill in "invitation[invitee_email]" with "test@" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When possible, we prefer the user-facing label text.
@@ -62,6 +62,18 @@ Feature: Invite requests | |||
And I should not see "Sorry, you have no unsent invitations right now." | |||
And I should see "You have 2 open invitations and 0 that have been sent but not yet used." | |||
|
|||
Scenario: User can fail to send an invitation to an invalid email address | |||
|
|||
Given invitations are required |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at Scenario: A user can manage their invitations
below, I think we can simplify this:
Given I am logged in as "user1"
And "user1" has "1" invitation
And I am on user1's manage invitations page
When I follow the link for "user1" first invite
And I fill in etc
@@ -62,16 +62,14 @@ Feature: Invite requests | |||
And I should not see "Sorry, you have no unsent invitations right now." | |||
And I should see "You have 2 open invitations and 0 that have been sent but not yet used." | |||
|
|||
Scenario: User can fail to send an invitation to an invalid email address | |||
Scenario: User can see an error after trying to invide an invalid email address |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops. I think I have some more things to fix anyway, and will fix the typo along them later.
|
||
it "raises an exception when the external author name is invalid and email is blank" do | ||
expect do | ||
@sp.parse_author("", "!!!!", "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RSpec/InstanceVariable: Avoid instance variables – use let, a method call, or a local variable (if possible).
|
||
it "raises an exception when the external author name is blank and email is invalid" do | ||
expect do | ||
@sp.parse_author("", "", "not_email") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RSpec/InstanceVariable: Avoid instance variables – use let, a method call, or a local variable (if possible).
|
||
it "raises an exception when the external author name and email are invalid" do | ||
expect do | ||
@sp.parse_author("", "!!!!", "not_email") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RSpec/InstanceVariable: Avoid instance variables – use let, a method call, or a local variable (if possible).
end | ||
|
||
it "ignores invalid letters in the external author name" do | ||
results = @sp.parse_author("", "Author!! Name!!", "[email protected]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RSpec/InstanceVariable: Avoid instance variables – use let, a method call, or a local variable (if possible).
end | ||
|
||
it "ignores the external author name when it is invalid" do | ||
results = @sp.parse_author("", "!!!!", "[email protected]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RSpec/InstanceVariable: Avoid instance variables – use let, a method call, or a local variable (if possible).
it "gives the same external author object for the same email" do | ||
res1 = @sp.parse_author("", "Author Name", "[email protected]") | ||
res2 = @sp.parse_author("", "Author Name Second", "[email protected]") | ||
res3 = @sp.parse_author("", "Author!! Name!!", "[email protected]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RSpec/InstanceVariable: Avoid instance variables – use let, a method call, or a local variable (if possible).
|
||
it "gives the same external author object for the same email" do | ||
res1 = @sp.parse_author("", "Author Name", "[email protected]") | ||
res2 = @sp.parse_author("", "Author Name Second", "[email protected]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RSpec/InstanceVariable: Avoid instance variables – use let, a method call, or a local variable (if possible).
end | ||
|
||
it "gives the same external author object for the same email" do | ||
res1 = @sp.parse_author("", "Author Name", "[email protected]") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RSpec/InstanceVariable: Avoid instance variables – use let, a method call, or a local variable (if possible).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's possible to replace the instance variable with let
. Not sure if it's a good idea to do it now, because that would be an unrelated refactoring that would touch most of the methods here.
Pull Request Checklist
as the first thing in your pull request title (e.g.
AO3-1234 Fix thing
)until they are reviewed and merged before creating new pull requests.
Issue
https://otwarchive.atlassian.net/browse/AO3-3997
Purpose
Fix additional issues found in AO3-3997, after #4390
Testing Instructions
References
Comments in the Jira link above, particularly https://otwarchive.atlassian.net/browse/AO3-3997?focusedCommentId=360179 .
Credit
Potpotkettle (they/them)