Skip to content

Commit

Permalink
SONAR-8361 WS api/components/search_projects return isFavorite
Browse files Browse the repository at this point in the history
  • Loading branch information
teryk committed Nov 8, 2016
1 parent f98d0b6 commit aee1bfc
Show file tree
Hide file tree
Showing 11 changed files with 103 additions and 102 deletions.
Expand Up @@ -22,6 +22,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.sonar.api.measures.Metric;

import static com.google.common.base.Preconditions.checkState;
Expand All @@ -32,7 +33,7 @@
public class ProjectMeasuresQuery {
private List<MetricCriterion> metricCriteria = new ArrayList<>();
private Metric.Level qualityGateStatus;
private List<String> projectUuids = null;
private Set<String> projectUuids = null;

public ProjectMeasuresQuery addMetricCriterion(MetricCriterion metricCriterion) {
this.metricCriteria.add(metricCriterion);
Expand All @@ -57,7 +58,7 @@ public Metric.Level getQualityGateStatus() {
return qualityGateStatus;
}

public ProjectMeasuresQuery setProjectUuids(List<String> projectUuids) {
public ProjectMeasuresQuery setProjectUuids(Set<String> projectUuids) {
this.projectUuids = requireNonNull(projectUuids);
return this;
}
Expand All @@ -66,7 +67,7 @@ public boolean doesFilterOnProjectUuids() {
return projectUuids != null;
}

public List<String> getProjectUuids() {
public Set<String> getProjectUuids() {
return requireNonNull(projectUuids);
}

Expand Down
Expand Up @@ -37,7 +37,6 @@ protected void configureModule() {
UpdateKeyAction.class,
BulkUpdateKeyAction.class,
SearchProjectsAction.class,
ProjectMeasuresQueryFactory.class,
ProjectMeasuresQueryValidator.class);
}
}
Expand Up @@ -21,58 +21,46 @@
package org.sonar.server.component.ws;

import com.google.common.base.Splitter;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.measures.Metric.Level;
import org.sonar.api.resources.Qualifiers;
import org.sonar.core.util.stream.Collectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.property.PropertyQuery;
import org.sonar.server.component.es.ProjectMeasuresQuery;
import org.sonar.server.user.UserSession;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Locale.ENGLISH;
import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
import static org.sonar.server.component.es.ProjectMeasuresQuery.MetricCriterion;
import static org.sonar.server.component.es.ProjectMeasuresQuery.Operator;

public class ProjectMeasuresQueryFactory {
class ProjectMeasuresQueryFactory {
private static final Splitter CRITERIA_SPLITTER = Splitter.on(Pattern.compile("and", Pattern.CASE_INSENSITIVE));
private static final Pattern CRITERIA_PATTERN = Pattern.compile("(\\w+)\\s*([<>]?[=]?)\\s*(\\w+)");
private static final String IS_FAVORITE_CRITERION = "isFavorite";

private final DbClient dbClient;
private final UserSession userSession;

public ProjectMeasuresQueryFactory(DbClient dbClient, UserSession userSession) {
this.dbClient = dbClient;
this.userSession = userSession;
private ProjectMeasuresQueryFactory() {
// prevent instantiation
}

ProjectMeasuresQuery newProjectMeasuresQuery(DbSession dbSession, String filter) {
static ProjectMeasuresQuery newProjectMeasuresQuery(String filter, Set<String> favoriteProjectUuids) {
if (StringUtils.isBlank(filter)) {
return new ProjectMeasuresQuery();
}

ProjectMeasuresQuery query = new ProjectMeasuresQuery();

CRITERIA_SPLITTER.split(filter)
.forEach(criteria -> processCriterion(dbSession, criteria, query));
.forEach(criteria -> processCriterion(criteria, query, favoriteProjectUuids));
return query;
}

private void processCriterion(DbSession dbSession, String rawCriterion, ProjectMeasuresQuery query) {
private static void processCriterion(String rawCriterion, ProjectMeasuresQuery query, Set<String> favoriteProjectUuids) {
String criterion = rawCriterion.trim();

try {
if (IS_FAVORITE_CRITERION.equalsIgnoreCase(criterion)) {
query.setProjectUuids(searchFavoriteUuids(dbSession));
query.setProjectUuids(favoriteProjectUuids);
return;
}

Expand All @@ -93,20 +81,4 @@ private void processCriterion(DbSession dbSession, String rawCriterion, ProjectM
}
}

private List<String> searchFavoriteUuids(DbSession dbSession) {
List<Long> favoriteDbIds = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setUserId(userSession.getUserId())
.setKey("favourite")
.build(), dbSession)
.stream()
.map(PropertyDto::getResourceId)
.collect(Collectors.toList());

return dbClient.componentDao().selectByIds(dbSession, favoriteDbIds)
.stream()
.filter(dbComponent -> Qualifiers.PROJECT.equals(dbComponent.qualifier()))
.map(ComponentDto::uuid)
.collect(Collectors.toList());
}

}
Expand Up @@ -25,28 +25,35 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Stream;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.core.util.stream.Collectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.property.PropertyDto;
import org.sonar.db.property.PropertyQuery;
import org.sonar.server.component.es.ProjectMeasuresIndex;
import org.sonar.server.component.es.ProjectMeasuresQuery;
import org.sonar.server.es.Facets;
import org.sonar.server.es.SearchIdResult;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Common;
import org.sonarqube.ws.WsComponents.Component;
import org.sonarqube.ws.WsComponents.SearchProjectsWsResponse;
import org.sonarqube.ws.client.component.SearchProjectsRequest;

import static com.google.common.base.MoreObjects.firstNonNull;
import static org.sonar.server.component.es.ProjectMeasuresIndex.SUPPORTED_FACETS;
import static org.sonar.server.component.ws.ProjectMeasuresQueryFactory.newProjectMeasuresQuery;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_FILTER;
import static org.sonarqube.ws.client.component.SearchProjectsRequest.DEFAULT_PAGE_SIZE;
Expand All @@ -56,14 +63,13 @@ public class SearchProjectsAction implements ComponentsWsAction {
private final DbClient dbClient;
private final ProjectMeasuresIndex index;
private final ProjectMeasuresQueryValidator queryValidator;
private final ProjectMeasuresQueryFactory queryFactory;
private final UserSession userSession;

public SearchProjectsAction(DbClient dbClient, ProjectMeasuresIndex index, ProjectMeasuresQueryFactory queryFactory,
ProjectMeasuresQueryValidator queryValidator) {
public SearchProjectsAction(DbClient dbClient, ProjectMeasuresIndex index, ProjectMeasuresQueryValidator queryValidator, UserSession userSession) {
this.dbClient = dbClient;
this.index = index;
this.queryFactory = queryFactory;
this.queryValidator = queryValidator;
this.userSession = userSession;
}

@Override
Expand Down Expand Up @@ -93,25 +99,44 @@ public void handle(Request httpRequest, Response httpResponse) throws Exception

private SearchProjectsWsResponse doHandle(SearchProjectsRequest request) {
try (DbSession dbSession = dbClient.openSession(false)) {
SearchResults searchResults = searchProjects(dbSession, request);
SearchResults searchResults = searchData(dbSession, request);

return buildResponse(request, searchResults);
}
}

private SearchResults searchProjects(DbSession dbSession, SearchProjectsRequest request) {
private SearchResults searchData(DbSession dbSession, SearchProjectsRequest request) {
String filter = firstNonNull(request.getFilter(), "");
ProjectMeasuresQuery query = queryFactory.newProjectMeasuresQuery(dbSession, filter);

Set<String> favoriteProjectUuids = searchFavoriteProjects(dbSession);

ProjectMeasuresQuery query = newProjectMeasuresQuery(filter, favoriteProjectUuids);
queryValidator.validate(dbSession, query);

SearchIdResult<String> searchResults = index.search(query, new SearchOptions()
SearchIdResult<String> esResults = index.search(query, new SearchOptions()
.addFacets(request.getFacets())
.setPage(request.getPage(), request.getPageSize()));

Ordering<ComponentDto> ordering = Ordering.explicit(searchResults.getIds()).onResultOf(ComponentDto::uuid);
List<ComponentDto> projects = ordering.immutableSortedCopy(dbClient.componentDao().selectByUuids(dbSession, searchResults.getIds()));
Ordering<ComponentDto> ordering = Ordering.explicit(esResults.getIds()).onResultOf(ComponentDto::uuid);
List<ComponentDto> projects = ordering.immutableSortedCopy(dbClient.componentDao().selectByUuids(dbSession, esResults.getIds()));

return new SearchResults(projects, favoriteProjectUuids, esResults);
}

return new SearchResults(projects, searchResults);
private Set<String> searchFavoriteProjects(DbSession dbSession) {
List<Long> favoriteDbIds = dbClient.propertiesDao().selectByQuery(PropertyQuery.builder()
.setUserId(userSession.getUserId())
.setKey("favourite")
.build(), dbSession)
.stream()
.map(PropertyDto::getResourceId)
.collect(Collectors.toList());

return dbClient.componentDao().selectByIds(dbSession, favoriteDbIds)
.stream()
.filter(dbComponent -> Qualifiers.PROJECT.equals(dbComponent.qualifier()))
.map(ComponentDto::uuid)
.collect(Collectors.toSet());
}

private static SearchProjectsRequest toRequest(Request httpRequest) {
Expand All @@ -126,7 +151,7 @@ private static SearchProjectsRequest toRequest(Request httpRequest) {
}

private static SearchProjectsWsResponse buildResponse(SearchProjectsRequest request, SearchResults searchResults) {
Function<ComponentDto, Component> dbToWsComponent = new DbToWsComponent();
Function<ComponentDto, Component> dbToWsComponent = new DbToWsComponent(searchResults.favoriteProjectUuids);

return Stream.of(SearchProjectsWsResponse.newBuilder())
.map(response -> response.setPaging(Common.Paging.newBuilder()
Expand Down Expand Up @@ -205,9 +230,11 @@ public Common.FacetValue apply(Entry<String, Long> bucket) {

private static class DbToWsComponent implements Function<ComponentDto, Component> {
private final Component.Builder wsComponent;
private final Set<String> favoriteProjectUuids;

private DbToWsComponent() {
private DbToWsComponent(Set<String> favoriteProjectUuids) {
this.wsComponent = Component.newBuilder();
this.favoriteProjectUuids = favoriteProjectUuids;
}

@Override
Expand All @@ -217,17 +244,20 @@ public Component apply(ComponentDto dbComponent) {
.setId(dbComponent.uuid())
.setKey(dbComponent.key())
.setName(dbComponent.name())
.setIsFavorite(favoriteProjectUuids.contains(dbComponent.uuid()))
.build();
}
}

private static class SearchResults {
private final List<ComponentDto> projects;
private final Set<String> favoriteProjectUuids;
private final Facets facets;
private final int total;

private SearchResults(List<ComponentDto> projects, SearchIdResult<String> searchResults) {
private SearchResults(List<ComponentDto> projects, Set<String> favoriteProjectUuids, SearchIdResult<String> searchResults) {
this.projects = projects;
this.favoriteProjectUuids = favoriteProjectUuids;
this.total = (int) searchResults.getTotal();
this.facets = searchResults.getFacets();
}
Expand Down
Expand Up @@ -8,17 +8,20 @@
{
"id": "AU-Tpxb--iU5OvuD2FLy",
"key": "my_project",
"name": "My Project 1"
"name": "My Project 1",
"isFavorite": true
},
{
"id": "AU-TpxcA-iU5OvuD2FLz",
"key": "another_project",
"name": "My Project 2"
"name": "My Project 2",
"isFavorite": false
},
{
"id": "AU-TpxcA-iU5OvuD2FL0",
"key": "third_project",
"name": "My Project 3"
"name": "My Project 3",
"isFavorite": false
}
]
}
Expand Up @@ -39,6 +39,7 @@
import org.sonar.server.tester.UserSessionRule;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -203,7 +204,7 @@ public void filter_on_ids() {
newDoc("P1", "K1", "N1"),
newDoc("P2", "K2", "N2"),
newDoc("P3", "K3", "N3"));
ProjectMeasuresQuery esQuery = new ProjectMeasuresQuery().setProjectUuids(newArrayList("P1", "P3"));
ProjectMeasuresQuery esQuery = new ProjectMeasuresQuery().setProjectUuids(newHashSet("P1", "P3"));

List<String> result = underTest.search(esQuery, new SearchOptions()).getIds();

Expand Down
Expand Up @@ -29,6 +29,6 @@ public class ComponentsWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new ComponentsWsModule().configure(container);
assertThat(container.size()).isEqualTo(13 + 2);
assertThat(container.size()).isEqualTo(12 + 2);
}
}

0 comments on commit aee1bfc

Please sign in to comment.