def index
@search = Search.new(search_results)
render json: @search, serializer: REST::SearchSerializer
+
+ # TODO: in the front end, these will show a toast that is only barely helpful
+ # TODO: semantics?
+
+ # user searched with a prefix that does exist
+ rescue Mastodon::SyntaxError
+ unprocessable_entity
+ # user searched for posts from an account the instance is not aware of
+ rescue ActiveRecord::NotFound
+ not_found
end
private
case prefix
when 'from'
@filter = :account_id
- username, domain = term.split('@')
- account = Account.find_remote(username, domain)
-
- raise "Account not found: #{term}" unless account
-
+ username, domain = Account.validate_account_string!(term)
+ account = Account.find_local_or_remote!(username, domain)
@term = account.id
+ # TODO: consider, instead of erroring on non-prefixes, treating them as words to search for. motivating case: searching for `https://.*`
else
- raise "Unknown prefix: #{prefix}"
+ raise Mastodon::SyntaxError, "Unknown prefix: #{prefix}"
end
end
end
find_remote(username, domain) || raise(ActiveRecord::RecordNotFound)
end
+ def find_local_or_remote!(username, domain)
+ find_local_or_remote(username, domain) || raise(ActiveRecord::RecordNotFound)
+ end
+
def representative
Account.find(-99)
rescue ActiveRecord::RecordNotFound
def find_remote(username, domain)
AccountFinder.new(username, domain).account
end
+
+ def find_local_or_remote(username, domain)
+ TagManager.instance.local_domain?(domain) ? find_local(username) : find_remote(username, domain)
+ end
+
+ def validate_account_string!(account_string)
+ match = ACCOUNT_STRING_RE.match(account_string)
+ raise Mastodon::SyntaxError if match.nil? || match[:username].nil?
+
+ [match[:username], match[:domain]]
+ end
end
+ # TODO: where should this go?
+ #
+ # this is adapted from MENTION_RE to
+ # + capture only a mention,
+ # + not require the mention to begin with an @,
+ # + not match if there is anything surrounding the mention, and
+ # + add named subgroup matches
+ # it would be ideal to explicitly refer to MENTION_RE, or a more fundamental regexp that we refactor MENTION_RE to incorporate
+ ACCOUNT_STRING_RE = /^@?(?<username>#{Account::USERNAME_RE})(?:@(?<domain>[[:word:]\.\-]+[[:word:]]+))?$/i
+
class AccountFinder
attr_reader :username, :domain
class StreamValidationError < ValidationError; end
class RaceConditionError < Error; end
class RateLimitExceededError < Error; end
+ class SyntaxError < Error; end
class UnexpectedResponseError < Error
attr_reader :response