All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.server.issue.ws.SearchResponseLoader Maven / Gradle / Ivy

There is a newer version: 7.2.1
Show newest version
/*
 * SonarQube
 * Copyright (C) 2009-2016 SonarSource SA
 * mailto:contact AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.server.issue.ws;

import com.google.common.collect.MultimapBuilder;
import com.google.common.collect.SetMultimap;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
import org.sonar.api.rule.RuleKey;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.protobuf.DbIssues;
import org.sonar.server.es.Facets;
import org.sonar.server.issue.ActionService;
import org.sonar.server.issue.IssueCommentService;
import org.sonar.server.issue.IssueService;
import org.sonarqube.ws.client.issue.IssueFilterParameters;

import static com.google.common.collect.Lists.newArrayList;
import static org.sonar.server.issue.ws.SearchAdditionalField.ACTIONS;
import static org.sonar.server.issue.ws.SearchAdditionalField.COMMENTS;
import static org.sonar.server.issue.ws.SearchAdditionalField.RULES;
import static org.sonar.server.issue.ws.SearchAdditionalField.TRANSITIONS;
import static org.sonar.server.issue.ws.SearchAdditionalField.USERS;

/**
 * Loads all the information required for the response of api/issues/search.
 */
public class SearchResponseLoader {

  private final DbClient dbClient;
  private final IssueService issueService;
  private final ActionService actionService;
  private final IssueCommentService commentService;

  public SearchResponseLoader(DbClient dbClient, IssueService issueService, ActionService actionService, IssueCommentService commentService) {
    this.dbClient = dbClient;
    this.issueService = issueService;
    this.actionService = actionService;
    this.commentService = commentService;
  }

  /**
   * The issue keys are given by the multi-criteria search in Elasticsearch index.
   */
  public SearchResponseData load(Collector collector, @Nullable Facets facets) {
    DbSession dbSession = dbClient.openSession(false);
    try {
      SearchResponseData result = new SearchResponseData(dbClient.issueDao().selectByOrderedKeys(dbSession, collector.getIssueKeys()));
      collector.collect(result.getIssues());

      loadRules(collector, dbSession, result);
      // order is important - loading of comments complete the list of users: loadComments() is
      // before loadUsers()
      loadComments(collector, dbSession, result);
      loadUsers(collector, dbSession, result);
      loadComponents(collector, dbSession, result);
      loadActionsAndTransitions(collector, result);
      completeTotalEffortFromFacet(facets, result);
      return result;
    } finally {
      dbClient.closeSession(dbSession);
    }
  }

  private void loadUsers(Collector collector, DbSession dbSession, SearchResponseData result) {
    if (collector.contains(USERS)) {
      result.setUsers(dbClient.userDao().selectByLogins(dbSession, collector.get(USERS)));
    }
  }

  private void loadComments(Collector collector, DbSession dbSession, SearchResponseData result) {
    if (collector.contains(COMMENTS)) {
      List comments = dbClient.issueChangeDao().selectByTypeAndIssueKeys(dbSession, collector.getIssueKeys(), IssueChangeDto.TYPE_COMMENT);
      result.setComments(comments);
      for (IssueChangeDto comment : comments) {
        collector.add(USERS, comment.getUserLogin());
        if (commentService.canEditOrDelete(comment)) {
          result.addUpdatableComment(comment.getKey());
        }
      }
    }
  }

  private void loadRules(Collector collector, DbSession dbSession, SearchResponseData result) {
    if (collector.contains(RULES)) {
      result.setRules(dbClient.ruleDao().selectByKeys(dbSession, collector.get(RULES)));
    }
  }

  private void loadComponents(Collector collector, DbSession dbSession, SearchResponseData result) {
    // always load components and projects, because some issue fields still relate to component ids/keys.
    // They should be dropped but are kept for backward-compatibility (see SearchResponseFormat)
    result.addComponents(dbClient.componentDao().selectByUuids(dbSession, collector.getComponentUuids()));
    result.addComponents(dbClient.componentDao().selectSubProjectsByComponentUuids(dbSession, collector.getComponentUuids()));
    for (ComponentDto component : result.getComponents()) {
      collector.addProjectUuid(component.projectUuid());
    }
    List projects = dbClient.componentDao().selectByUuids(dbSession, collector.getProjectUuids());
    result.addComponents(projects);
  }

  private void loadActionsAndTransitions(Collector collector, SearchResponseData result) {
    if (collector.contains(ACTIONS) || collector.contains(TRANSITIONS)) {
      for (IssueDto dto : result.getIssues()) {
        // so that IssueDto can be used.
        if (collector.contains(ACTIONS)) {
          result.addActions(dto.getKey(), actionService.listAvailableActions(dto.toDefaultIssue()));
        }
        if (collector.contains(TRANSITIONS)) {
          // TODO workflow and action engines must not depend on org.sonar.api.issue.Issue but on a generic interface
          DefaultIssue issue = dto.toDefaultIssue();
          result.addTransitions(issue.key(), issueService.listTransitions(issue));
        }
      }
    }
  }

  private void completeTotalEffortFromFacet(@Nullable Facets facets, SearchResponseData result) {
    if (facets != null) {
      Map effortFacet = facets.get(IssueFilterParameters.FACET_MODE_EFFORT);
      if (effortFacet != null) {
        result.setEffortTotal(effortFacet.get(Facets.TOTAL));
      }
    }
  }

  /**
   * Collects the keys of all the data to be loaded (users, rules, ...)
   */
  public static class Collector {
    private final EnumSet fields;
    private final SetMultimap fieldValues = MultimapBuilder.enumKeys(SearchAdditionalField.class).hashSetValues().build();
    private final Set componentUuids = new HashSet<>();
    private final Set projectUuids = new HashSet<>();
    private final List issueKeys;

    public Collector(EnumSet fields, List issueKeys) {
      this.fields = fields;
      this.issueKeys = issueKeys;
    }

    void collect(List issues) {
      for (IssueDto issue : issues) {
        componentUuids.add(issue.getComponentUuid());
        projectUuids.add(issue.getProjectUuid());
        add(RULES, issue.getRuleKey());
        add(USERS, issue.getAssignee());
        collectComponentsFromIssueLocations(issue);
      }
    }

    private void collectComponentsFromIssueLocations(IssueDto issue) {
      DbIssues.Locations locations = issue.parseLocations();
      if (locations != null) {
        for (DbIssues.Flow flow : locations.getFlowList()) {
          for (DbIssues.Location location : flow.getLocationList()) {
            if (location.hasComponentId()) {
              componentUuids.add(location.getComponentId());
            }
          }
        }
      }
    }

    public void add(SearchAdditionalField key, @Nullable Object value) {
      if (value != null) {
        fieldValues.put(key, value);
      }
    }

    public void addComponentUuids(@Nullable Collection uuids) {
      if (uuids != null) {
        this.componentUuids.addAll(uuids);
      }
    }

    public void addProjectUuid(String uuid) {
      this.projectUuids.add(uuid);
    }

    public void addProjectUuids(@Nullable Collection uuids) {
      if (uuids != null) {
        this.projectUuids.addAll(uuids);
      }
    }

    public void addAll(SearchAdditionalField key, @Nullable Iterable values) {
      if (values != null) {
        for (Object value : values) {
          add(key, value);
        }
      }
    }

     List get(SearchAdditionalField key) {
      return newArrayList((Set) fieldValues.get(key));
    }

    boolean contains(SearchAdditionalField field) {
      return fields.contains(field);
    }

    public List getIssueKeys() {
      return issueKeys;
    }

    public Set getComponentUuids() {
      return componentUuids;
    }

    public Set getProjectUuids() {
      return projectUuids;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy