self.response_body = Oj.dump(response.body)
self.status = response.status
+ rescue ActiveRecord::RecordInvalid => e
+ render json: ValidationErrorFormatter.new(e, :'account.username' => :username, :'invite_request.text' => :reason).as_json, status: :unprocessable_entity
end
def follow
--- /dev/null
+# frozen_string_literal: true
+
+class ValidationErrorFormatter
+ def initialize(error, aliases = {})
+ @error = error
+ @aliases = aliases
+ end
+
+ def as_json
+ { error: @error.to_s, details: details }
+ end
+
+ private
+
+ def details
+ h = {}
+
+ errors.details.each_pair do |attribute_name, attribute_errors|
+ messages = errors.messages[attribute_name]
+
+ h[@aliases[attribute_name] || attribute_name] = attribute_errors.map.with_index do |error, index|
+ { error: 'ERR_' + error[:error].to_s.upcase, description: messages[index] }
+ end
+ end
+
+ h
+ end
+
+ def errors
+ @errors ||= @error.record.errors
+ end
+end
class BlacklistedEmailValidator < ActiveModel::Validator
def validate(user)
- return if user.valid_invitation?
+ return if user.valid_invitation? || user.email.blank?
@email = user.email
- user.errors.add(:email, I18n.t('users.blocked_email_provider')) if blocked_email?
+ user.errors.add(:email, :blocked) if blocked_email?
end
private
class EmailMxValidator < ActiveModel::Validator
def validate(user)
+ return if user.email.blank?
+
domain = get_domain(user.email)
- if domain.nil?
- user.errors.add(:email, I18n.t('users.invalid_email'))
+ if domain.blank?
+ user.errors.add(:email, :invalid)
else
ips, hostnames = resolve_mx(domain)
+
if ips.empty?
- user.errors.add(:email, I18n.t('users.invalid_email_mx'))
+ user.errors.add(:email, :unreachable)
elsif on_blacklist?(hostnames + ips)
- user.errors.add(:email, I18n.t('users.blocked_email_provider'))
+ user.errors.add(:email, :blocked)
end
end
end
class NoteLengthValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
- record.errors.add(attribute, I18n.t('statuses.over_character_limit', max: options[:maximum])) if too_long?(value)
+ record.errors.add(attribute, :too_long, message: I18n.t('statuses.over_character_limit', max: options[:maximum]), count: options[:maximum]) if too_long?(value)
end
private
class UniqueUsernameValidator < ActiveModel::Validator
def validate(account)
- return if account.username.nil?
+ return if account.username.blank?
normalized_username = account.username.downcase
normalized_domain = account.domain&.downcase
class UnreservedUsernameValidator < ActiveModel::Validator
def validate(account)
@username = account.username
- return if @username.nil?
- account.errors.add(:username, I18n.t('accounts.reserved_username')) if reserved_username?
+ return if @username.blank?
+
+ account.errors.add(:username, :reserved) if reserved_username?
end
private
poll:
expires_at: Deadline
options: Choices
+ user:
+ agreement: Service agreement
+ email: E-mail address
+ locale: Locale
+ password: Password
+ user/account:
+ username: Username
+ user/invite_request:
+ text: Reason
errors:
models:
account:
attributes:
username:
- invalid: only letters, numbers and underscores
+ invalid: must contain only letters, numbers and underscores
+ reserved: is reserved
status:
attributes:
reblog:
taken: of status already exists
+ user:
+ attributes:
+ email:
+ blocked: uses a disallowed e-mail provider
+ unreachable: does not seem to exist
other: Toots
posts_tab_heading: Toots
posts_with_replies: Toots and replies
- reserved_username: The username is reserved
roles:
admin: Admin
bot: Bot
tips: Tips
title: Welcome aboard, %{name}!
users:
- blocked_email_provider: This e-mail provider isn't allowed
follow_limit_reached: You cannot follow more than %{limit} people
generic_access_help_html: Trouble accessing your account? You may get in touch with %{email} for assistance
- invalid_email: The e-mail address is invalid
- invalid_email_mx: The e-mail address does not seem to exist
invalid_otp_token: Invalid two-factor code
invalid_sign_in_token: Invalid security code
otp_lost_help_html: If you lost access to both, you may get in touch with %{email}
end
it 'shows a login error' do
- expect(flash[:alert]).to match I18n.t('devise.failure.invalid', authentication_keys: 'Email')
+ expect(flash[:alert]).to match I18n.t('devise.failure.invalid', authentication_keys: I18n.t('activerecord.attributes.user.email'))
end
it "doesn't log the user in" do
end
it 'shows a login error' do
- expect(flash[:alert]).to match I18n.t('devise.failure.invalid', authentication_keys: 'Email')
+ expect(flash[:alert]).to match I18n.t('devise.failure.invalid', authentication_keys: I18n.t('activerecord.attributes.user.email'))
end
it "doesn't log the user in" do
let(:blocked_email) { true }
it 'calls errors.add' do
- expect(errors).to have_received(:add).with(:email, I18n.t('users.blocked_email_provider'))
+ expect(errors).to have_received(:add).with(:email, :blocked)
end
end
let(:blocked_email) { false }
it 'not calls errors.add' do
- expect(errors).not_to have_received(:add).with(:email, I18n.t('users.blocked_email_provider'))
+ expect(errors).not_to have_received(:add).with(:email, :blocked)
end
end
end
let(:account) { double(username: username, errors: errors) }
let(:errors ) { double(add: nil) }
- context '@username.nil?' do
+ context '@username.blank?' do
let(:username) { nil }
it 'not calls errors.add' do
end
end
- context '!@username.nil?' do
- let(:username) { '' }
+ context '!@username.blank?' do
+ let(:username) { 'f' }
context 'reserved_username?' do
let(:reserved_username) { true }
it 'calls erros.add' do
- expect(errors).to have_received(:add).with(:username, I18n.t('accounts.reserved_username'))
+ expect(errors).to have_received(:add).with(:username, :reserved)
end
end