/jira-project/jira-components/jira-core/src/main/java/com/atlassian/jira/mention/SortedMentionableUserSearcher.java
Java | 221 lines | 184 code | 22 blank | 15 comment | 13 complexity | 2f33c3715cfc8c50653c8cbd1f3aab91 MD5 | raw file
Possible License(s): Apache-2.0
- package com.atlassian.jira.mention;
- import com.atlassian.crowd.embedded.api.User;
- import com.atlassian.jira.bc.ServiceOutcome;
- import com.atlassian.jira.bc.issue.comment.CommentService;
- import com.atlassian.jira.bc.issue.vote.VoteService;
- import com.atlassian.jira.bc.issue.watcher.WatcherService;
- import com.atlassian.jira.bc.user.search.UserMatcherPredicate;
- import com.atlassian.jira.bc.user.search.UserSearchParams;
- import com.atlassian.jira.bc.user.search.UserSearchService;
- import com.atlassian.jira.issue.Issue;
- import com.atlassian.jira.issue.comments.Comment;
- import com.atlassian.jira.permission.ProjectPermissions;
- import com.atlassian.jira.security.PermissionManager;
- import com.atlassian.jira.security.Permissions;
- import com.atlassian.jira.user.ApplicationUser;
- import com.atlassian.jira.user.ApplicationUsers;
- import com.atlassian.jira.user.ImmutableUserIssueRelevance;
- import com.atlassian.jira.user.IssueInvolvement;
- import com.atlassian.jira.user.UserIssueRelevance;
- import com.atlassian.jira.util.I18nHelper;
- import com.atlassian.jira.util.lang.Pair;
- import com.google.common.collect.ImmutableList;
- import org.apache.commons.lang.StringUtils;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.Comparator;
- import java.util.Date;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Map;
- import java.util.Optional;
- import java.util.Set;
- import java.util.function.BinaryOperator;
- import java.util.function.Predicate;
- import java.util.stream.Collectors;
- import static com.atlassian.jira.util.dbc.Assertions.notNull;
- public class SortedMentionableUserSearcher implements MentionableUserSearcher {
- private static final Date EPOCH = new Date(0);
- private final PermissionManager permissionManager;
- private final I18nHelper i18n;
- private final CommentService commentService;
- private final WatcherService watcherService;
- private final VoteService voteService;
- private final UserSearchService userSearchService;
- public SortedMentionableUserSearcher(
- final I18nHelper i18n,
- final PermissionManager permissionManager,
- final CommentService commentService,
- final WatcherService watcherService,
- final VoteService voteService,
- final UserSearchService userSearchService) {
- this.permissionManager = permissionManager;
- this.commentService = commentService;
- this.watcherService = watcherService;
- this.voteService = voteService;
- this.userSearchService = userSearchService;
- this.i18n = notNull(i18n);
- }
- @Override
- public List<UserIssueRelevance> findRelatedUsersToMention(
- final String query,
- final Issue issue,
- final ApplicationUser requestingUser,
- final int maxResults) {
- if (issue == null || !permissionManager.hasPermission(Permissions.USER_PICKER, requestingUser)) {
- return ImmutableList.of();
- }
- final String lowercaseQuery = StringUtils.isEmpty(query) ? "" : query.toLowerCase(i18n.getLocale());
- final List<Comment> visibleComments = commentService.getCommentsForUser(requestingUser, issue).stream()
- .filter(comment -> comment.getAuthorApplicationUser() != null)
- .collect(Collectors.toList());
- final Optional<ApplicationUser> assignee = Optional.ofNullable(issue.getAssignee());
- final Optional<ApplicationUser> reporter = Optional.ofNullable(issue.getReporter());
- final List<ApplicationUser> usersFromRoleMention = getUsersFromRoleMention(lowercaseQuery, assignee, reporter);
- final Map<ApplicationUser, Date> userLatestCommentDates = visibleComments.stream()
- .collect(Collectors.groupingBy(
- Comment::getAuthorApplicationUser,
- Collectors.reducing(EPOCH, Comment::getCreated, BinaryOperator.maxBy(Comparator.naturalOrder()))
- ));
- final List<ApplicationUser> watchers = getWatchers(requestingUser, issue);
- final List<ApplicationUser> voters = getVoters(requestingUser, issue);
- final Set<ApplicationUser> involvedUsers = new HashSet<>();
- assignee.ifPresent(involvedUsers::add);
- reporter.ifPresent(involvedUsers::add);
- involvedUsers.addAll(userLatestCommentDates.keySet());
- involvedUsers.addAll(watchers);
- involvedUsers.addAll(voters);
- final List<ApplicationUser> filteredInvolvedUsers = involvedUsers.stream()
- .filter(new UserMatcherPredicate(lowercaseQuery, false)::apply)
- .collect(Collectors.toList());
- final Set<ApplicationUser> matchingUsers = new HashSet<>();
- matchingUsers.addAll(usersFromRoleMention);
- matchingUsers.addAll(filteredInvolvedUsers);
- // If we haven't found enough users involved with the issue, search for
- // users who match the query
- if (matchingUsers.size() < maxResults) {
- // We ask for maxResults (and not maxResults - matchingUsers.size())
- // as there may be overlap with already added users
- final List<ApplicationUser> usersFromSearch = searchForUsers(lowercaseQuery, issue, maxResults);
- matchingUsers.addAll(usersFromSearch);
- }
- return getUserIssueRelevances(matchingUsers, issue, assignee, reporter, userLatestCommentDates, voters, watchers, maxResults);
- }
- private List<UserIssueRelevance> getUserIssueRelevances(
- final Set<ApplicationUser> matchingUsers,
- final Issue issue,
- final Optional<ApplicationUser> assignee,
- final Optional<ApplicationUser> reporter,
- final Map<ApplicationUser, Date> userLatestCommentDates,
- final List<ApplicationUser> voters,
- final List<ApplicationUser> watchers,
- final int maxResults) {
- final Set<ApplicationUser> watcherSet = new HashSet<>(watchers);
- final Set<ApplicationUser> voterSet = new HashSet<>(voters);
- return matchingUsers.stream()
- .map(user -> buildUserIssuerRelevance(user, issue, assignee, reporter, userLatestCommentDates, watcherSet, voterSet))
- .sorted()
- .limit(maxResults)
- .collect(Collectors.toList());
- }
- /**
- * Returns a list of users matching the role mentioned in the query
- * (i.e. returns the assignee if the query matches "assignee")
- *
- * @param lowercaseQuery The query to compare against roles
- * @param assignee The user to add to the list of matching users if the
- * query matches "assignee"
- * @param reporter The user to add to the list of matching users if the
- * query matches "reporter"
- * @return A list of users matching the role mentioned in the query
- */
- private List<ApplicationUser> getUsersFromRoleMention(
- final String lowercaseQuery,
- final Optional<ApplicationUser> assignee,
- final Optional<ApplicationUser> reporter) {
- final List<ApplicationUser> users = new ArrayList<>(2);
- if (queryMatchesInvolvement(lowercaseQuery, IssueInvolvement.REPORTER)) {
- reporter.ifPresent(users::add);
- }
- if (queryMatchesInvolvement(lowercaseQuery, IssueInvolvement.ASSIGNEE)) {
- assignee.ifPresent(users::add);
- }
- return users;
- }
- private boolean queryMatchesInvolvement(final String lowercaseQuery, final IssueInvolvement involvement) {
- return i18n.getText(involvement.getI18nKey())
- .toLowerCase(i18n.getLocale())
- .startsWith(lowercaseQuery);
- }
- private List<ApplicationUser> getVoters(final ApplicationUser requestingUser, final Issue issue) {
- final ServiceOutcome<Collection<ApplicationUser>> voters = voteService.viewVoters(issue, requestingUser);
- return voters.isValid() ? ImmutableList.copyOf(voters.getReturnedValue()) : ImmutableList.of();
- }
- private List<ApplicationUser> getWatchers(final ApplicationUser requestingUser, final Issue issue) {
- if (watcherService.isWatchingEnabled()) {
- final ServiceOutcome<Pair<Integer, List<ApplicationUser>>> watchers = watcherService.getWatchers(issue, requestingUser);
- if (watchers.isValid()) {
- return watchers.getReturnedValue().second();
- }
- }
- return ImmutableList.of();
- }
- private UserIssueRelevance buildUserIssuerRelevance(
- final ApplicationUser user,
- final Issue issue,
- final Optional<ApplicationUser> assignee,
- final Optional<ApplicationUser> reporter,
- final Map<ApplicationUser, Date> userLatestCommentDates,
- final Set<ApplicationUser> watchers,
- final Set<ApplicationUser> voters) {
- final ImmutableUserIssueRelevance.IssueUserBuilder issueUserBuilder = new ImmutableUserIssueRelevance.IssueUserBuilder()
- .withIssue(issue)
- .withUser(user);
- if (assignee.map(user::equals).orElse(false)) {
- issueUserBuilder.withAssigneeInvolvement();
- }
- if (reporter.map(user::equals).orElse(false)) {
- issueUserBuilder.withReporterInvolvement();
- }
- Optional.ofNullable(userLatestCommentDates.get(user))
- .ifPresent(issueUserBuilder::withCommenterInvolvement);
- if (watchers.contains(user)) {
- issueUserBuilder.withWatcherInvolvement();
- }
- if (voters.contains(user)) {
- issueUserBuilder.withVoterInvolvement();
- }
- return issueUserBuilder.build();
- }
- private List<ApplicationUser> searchForUsers(final String lowercaseQuery, final Issue issue, final int maxResults) {
- final Predicate<User> hasIssueBrowsePermission = user -> permissionManager.hasPermission(ProjectPermissions.BROWSE_PROJECTS, issue, ApplicationUsers.from(user));
- final UserSearchParams anyActiveUser = UserSearchParams.builder()
- .allowEmptyQuery(true)
- .filter(hasIssueBrowsePermission::test)
- .maxResults(maxResults)
- .build();
- return userSearchService.findUsers(lowercaseQuery, anyActiveUser);
- }
- }