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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import org.sonar.api.measures.Metric; import org.sonar.api.measures.Metric;


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


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


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


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


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


import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import java.util.List; import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.sonar.api.measures.Metric.Level; 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.component.es.ProjectMeasuresQuery;
import org.sonar.server.user.UserSession;


import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Locale.ENGLISH; import static java.util.Locale.ENGLISH;
import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY; 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.MetricCriterion;
import static org.sonar.server.component.es.ProjectMeasuresQuery.Operator; 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 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 Pattern CRITERIA_PATTERN = Pattern.compile("(\\w+)\\s*([<>]?[=]?)\\s*(\\w+)");
private static final String IS_FAVORITE_CRITERION = "isFavorite"; private static final String IS_FAVORITE_CRITERION = "isFavorite";


private final DbClient dbClient; private ProjectMeasuresQueryFactory() {
private final UserSession userSession; // prevent instantiation

public ProjectMeasuresQueryFactory(DbClient dbClient, UserSession userSession) {
this.dbClient = dbClient;
this.userSession = userSession;
} }


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


ProjectMeasuresQuery query = new ProjectMeasuresQuery(); ProjectMeasuresQuery query = new ProjectMeasuresQuery();


CRITERIA_SPLITTER.split(filter) CRITERIA_SPLITTER.split(filter)
.forEach(criteria -> processCriterion(dbSession, criteria, query)); .forEach(criteria -> processCriterion(criteria, query, favoriteProjectUuids));
return query; 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(); String criterion = rawCriterion.trim();


try { try {
if (IS_FAVORITE_CRITERION.equalsIgnoreCase(criterion)) { if (IS_FAVORITE_CRITERION.equalsIgnoreCase(criterion)) {
query.setProjectUuids(searchFavoriteUuids(dbSession)); query.setProjectUuids(favoriteProjectUuids);
return; 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.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collector; import java.util.stream.Collector;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.WebService.Param; import org.sonar.api.server.ws.WebService.Param;
import org.sonar.core.util.stream.Collectors;
import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto; 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.ProjectMeasuresIndex;
import org.sonar.server.component.es.ProjectMeasuresQuery; import org.sonar.server.component.es.ProjectMeasuresQuery;
import org.sonar.server.es.Facets; import org.sonar.server.es.Facets;
import org.sonar.server.es.SearchIdResult; import org.sonar.server.es.SearchIdResult;
import org.sonar.server.es.SearchOptions; import org.sonar.server.es.SearchOptions;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Common; import org.sonarqube.ws.Common;
import org.sonarqube.ws.WsComponents.Component; import org.sonarqube.ws.WsComponents.Component;
import org.sonarqube.ws.WsComponents.SearchProjectsWsResponse; import org.sonarqube.ws.WsComponents.SearchProjectsWsResponse;
import org.sonarqube.ws.client.component.SearchProjectsRequest; import org.sonarqube.ws.client.component.SearchProjectsRequest;


import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.MoreObjects.firstNonNull;
import static org.sonar.server.component.es.ProjectMeasuresIndex.SUPPORTED_FACETS; 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.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_FILTER; import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_FILTER;
import static org.sonarqube.ws.client.component.SearchProjectsRequest.DEFAULT_PAGE_SIZE; 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 DbClient dbClient;
private final ProjectMeasuresIndex index; private final ProjectMeasuresIndex index;
private final ProjectMeasuresQueryValidator queryValidator; private final ProjectMeasuresQueryValidator queryValidator;
private final ProjectMeasuresQueryFactory queryFactory; private final UserSession userSession;


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


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


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


return buildResponse(request, searchResults); return buildResponse(request, searchResults);
} }
} }


private SearchResults searchProjects(DbSession dbSession, SearchProjectsRequest request) { private SearchResults searchData(DbSession dbSession, SearchProjectsRequest request) {
String filter = firstNonNull(request.getFilter(), ""); 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); queryValidator.validate(dbSession, query);


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


Ordering<ComponentDto> ordering = Ordering.explicit(searchResults.getIds()).onResultOf(ComponentDto::uuid); Ordering<ComponentDto> ordering = Ordering.explicit(esResults.getIds()).onResultOf(ComponentDto::uuid);
List<ComponentDto> projects = ordering.immutableSortedCopy(dbClient.componentDao().selectByUuids(dbSession, searchResults.getIds())); 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) { 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) { 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()) return Stream.of(SearchProjectsWsResponse.newBuilder())
.map(response -> response.setPaging(Common.Paging.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 static class DbToWsComponent implements Function<ComponentDto, Component> {
private final Component.Builder wsComponent; private final Component.Builder wsComponent;
private final Set<String> favoriteProjectUuids;


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


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


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


import static com.google.common.collect.Lists.newArrayList; 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.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat; 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("P1", "K1", "N1"),
newDoc("P2", "K2", "N2"), newDoc("P2", "K2", "N2"),
newDoc("P3", "K3", "N3")); 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(); 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() { public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer(); ComponentContainer container = new ComponentContainer();
new ComponentsWsModule().configure(container); 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.