return value;
}
- public Collection<DTO> getByKeys(DbSession session, KEY... keys) {
+ public List<DTO> getByKeys(DbSession session, KEY... keys) {
return getByKeys(session, ImmutableList.<KEY>copyOf(keys));
}
- public Collection<DTO> getByKeys(DbSession session, Collection<KEY> keys) {
- Collection<DTO> results = new ArrayList<DTO>();
+ public List<DTO> getByKeys(DbSession session, Collection<KEY> keys) {
+ List<DTO> results = new ArrayList<DTO>();
for (KEY key : keys) {
results.add(this.getByKey(session, key));
}
// Synchronization methods
- protected ResultHandler getSynchronizationResultHandler(final DbSession session){
+ protected ResultHandler getSynchronizationResultHandler(final DbSession session) {
return new ResultHandler() {
@Override
public void handleResult(ResultContext resultContext) {
};
}
- protected Map getSynchronizationParams(Date date){
+ protected Map getSynchronizationParams(Date date) {
Map<String, Object> params = newHashMap();
params.put("date", new Timestamp(date.getTime()));
return params;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import org.apache.commons.lang.StringUtils;
-import org.elasticsearch.action.search.SearchResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.ServerComponent;
import org.sonar.api.rules.RuleFinder;
import org.sonar.api.user.User;
import org.sonar.api.user.UserFinder;
-import org.sonar.api.utils.Paging;
import org.sonar.api.web.UserRole;
import org.sonar.core.issue.DefaultIssueBuilder;
import org.sonar.core.issue.IssueNotifications;
import org.sonar.server.db.DbClient;
import org.sonar.server.issue.actionplan.ActionPlanService;
import org.sonar.server.issue.index.IssueIndex;
-import org.sonar.server.issue.index.IssueResult;
-import org.sonar.server.rule.index.RuleIndex;
import org.sonar.server.search.IndexClient;
import org.sonar.server.search.QueryContext;
import org.sonar.server.user.UserSession;
import javax.annotation.Nullable;
-
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
/**
* @since 3.6
return indexClient.get(IssueIndex.class).getByKey(key);
}
- public IssueResult search(IssueQuery query, QueryContext options) {
+ public org.sonar.server.search.Result<Issue> search(IssueQuery query, QueryContext options) {
IssueIndex issueIndex = indexClient.get(IssueIndex.class);
- SearchResponse esResults = issueIndex.search(query, options);
-
- // Extend the content of the resultSet to make an actual IssueResponse
- IssueResult result = new IssueResult(issueIndex, esResults);
- result.setPaging(Paging.create(
- options.getLimit(),
- (options.getOffset() * options.getLimit()) + 1,
- new Long(esResults.getHits().getTotalHits()).intValue()));
-
- // Insert the projects and component name;
- Set<RuleKey> ruleKeys = new HashSet<RuleKey>();
- Set<String> projectKeys = new HashSet<String>();
- Set<String> componentKeys = new HashSet<String>();
- Set<String> actionPlanKeys = new HashSet<String>();
- List<String> userLogins = new ArrayList<String>();
-
- DbSession session = dbClient.openSession(false);
- for (Issue issue : result.getHits()) {
- ruleKeys.add(issue.ruleKey());
- projectKeys.add(issue.projectKey());
- componentKeys.add(issue.componentKey());
- actionPlanKeys.add(issue.actionPlanKey());
- userLogins.add(issue.authorLogin());
- }
-
- try {
- // TODO Rule vs Rule problem
- indexClient.get(RuleIndex.class).getByKeys(ruleKeys);
- result.addProjects(dbClient.componentDao().getByKeys(session, projectKeys));
- result.addComponents(dbClient.componentDao().getByKeys(session, componentKeys));
- result.addUsers(userFinder.findByLogins(userLogins));
- result.addActionPlans(actionPlanService.findByKeys(actionPlanKeys));
- } finally {
- session.close();
- }
+ return issueIndex.search(query, options);
- return result;
}
}
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.AggregationBuilders;
+import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueQuery;
import org.sonar.api.web.UserRole;
import org.sonar.core.issue.db.IssueDto;
import java.util.Map;
import java.util.Set;
-public class IssueIndex extends BaseIndex<IssueDoc, IssueDto, String> {
+public class IssueIndex extends BaseIndex<Issue, IssueDto, String> {
public IssueIndex(IssueNormalizer normalizer, SearchClient client) {
super(IndexDefinition.ISSUES, normalizer, client);
return new IssueDoc(fields);
}
- public SearchResponse search(IssueQuery query, QueryContext options) {
+ public Result<Issue> search(IssueQuery query, QueryContext options) {
SearchRequestBuilder esSearch = getClient()
.prepareSearch(this.getIndexName())
// esSearch.addAggregation(AggregationBuilders.sum("totalDuration")
// .field(IssueNormalizer.IssueField.DEBT.field()));
- return getClient().execute(esSearch);
+ SearchResponse response = getClient().execute(esSearch);
+ return new Result<Issue>(this, response);
}
private void matchFilter(BoolFilterBuilder filter, IndexField field, Collection<?> values) {
import java.util.List;
import java.util.Map;
+@Deprecated
public class IssueResult extends Result<IssueDoc> implements IssueQueryResult {
private final Map<String, Rule> rules;
public void addComponents(Collection<ComponentDto> components) {
for (ComponentDto component : components) {
this.components.put(component.key(), component);
- }
+ }
}
public void addUsers(Collection<User> users) {
*/
package org.sonar.server.issue.ws;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.io.Resources;
-import org.sonar.api.component.Component;
import org.sonar.api.i18n.I18n;
-import org.sonar.api.issue.*;
+import org.sonar.api.issue.ActionPlan;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.issue.IssueComment;
+import org.sonar.api.issue.IssueQuery;
+import org.sonar.api.issue.IssueQueryResult;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.Severity;
-import org.sonar.api.rules.Rule;
import org.sonar.api.server.ws.Request;
-import org.sonar.api.server.ws.RequestHandler;
-import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.user.User;
+import org.sonar.api.user.UserFinder;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.Durations;
import org.sonar.api.utils.text.JsonWriter;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto;
+import org.sonar.core.persistence.DbSession;
import org.sonar.markdown.Markdown;
+import org.sonar.server.component.DefaultComponentFinder;
+import org.sonar.server.db.DbClient;
import org.sonar.server.issue.IssueService;
+import org.sonar.server.issue.actionplan.ActionPlanService;
import org.sonar.server.issue.filter.IssueFilterParameters;
-import org.sonar.server.issue.index.IssueResult;
-import org.sonar.server.search.FacetValue;
+import org.sonar.server.rule.Rule;
+import org.sonar.server.rule.RuleService;
import org.sonar.server.search.QueryContext;
import org.sonar.server.search.Result;
-import org.sonar.server.search.ws.SearchOptions;
+import org.sonar.server.search.ws.SearchRequestHandler;
import org.sonar.server.user.UserSession;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import static com.google.common.collect.Lists.newArrayList;
-public class SearchAction implements RequestHandler {
+public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
public static final String SEARCH_ACTION = "search";
private static final String EXTRA_FIELDS_PARAM = "extra_fields";
- public static final String PARAM_FACETS = "facets";
-
private final IssueService service;
private final IssueActionsWriter actionsWriter;
+
+ private final RuleService ruleService;
+ private final DbClient dbClient;
+ private final DefaultComponentFinder componentFinder;
+ private final ActionPlanService actionPlanService;
+ private final UserFinder userFinder;
private final I18n i18n;
private final Durations durations;
- public SearchAction(IssueService service, IssueActionsWriter actionsWriter, I18n i18n, Durations durations) {
+ public SearchAction(IssueService service, IssueActionsWriter actionsWriter, RuleService ruleService, DbClient dbClient, DefaultComponentFinder componentFinder,
+ ActionPlanService actionPlanService, UserFinder userFinder, I18n i18n, Durations durations) {
+ super(SEARCH_ACTION);
this.service = service;
this.actionsWriter = actionsWriter;
+ this.ruleService = ruleService;
+ this.dbClient = dbClient;
+ this.componentFinder = componentFinder;
+ this.actionPlanService = actionPlanService;
+ this.userFinder = userFinder;
this.i18n = i18n;
this.durations = durations;
}
- void define(WebService.NewController controller) {
- WebService.NewAction action = controller.createAction(SEARCH_ACTION)
- .setDescription("Get a list of issues. If the number of issues is greater than 10,000, only the first 10,000 ones are returned by the web service. " +
- "Requires Browse permission on project(s)")
+ @Override
+ protected void doDefinition(WebService.NewAction action) {
+ action.setDescription("Get a list of issues. If the number of issues is greater than 10,000, " +
+ "only the first 10,000 ones are returned by the web service. " +
+ "Requires Browse permission on project(s)")
.setSince("3.6")
.setHandler(this)
.setResponseExample(Resources.getResource(this.getClass(), "example-search.json"));
- // Add globalized search options. Will also support legacy params
- // Generic search parameters
- SearchOptions.definePageParams(action);
- SearchOptions.defineFieldsParam(action, Collections.<String>emptyList());
-
- // Issue-specific search parameters
- defineIssueSearchParameters(action);
-
- // Other parameters
- action.createParam(PARAM_FACETS)
- .setDescription("Compute predefined facets")
- .setBooleanPossibleValues()
- .setDefaultValue("false");
- }
-
- public static void defineIssueSearchParameters(WebService.NewAction action) {
-
action.createParam(IssueFilterParameters.ISSUES)
.setDescription("Comma-separated list of issue keys")
.setExampleValue("5bccd6e8-f525-43a2-8d76-fcb13dde79ef");
}
@Override
- public void handle(Request request, Response response) {
- IssueQuery query = createQuery(request);
- SearchOptions searchOptions = SearchOptions.create(request);
- QueryContext queryContext = new QueryContext();
- queryContext.setPage(searchOptions.page(), searchOptions.pageSize());
- queryContext.setFacet(request.mandatoryParamAsBoolean(PARAM_FACETS));
+ protected IssueQuery doQuery(Request request) {
+ IssueQuery.Builder builder = IssueQuery.builder()
+ .requiredRole(UserRole.USER)
+ .issueKeys(request.paramAsStrings(IssueFilterParameters.ISSUES))
+ .severities(request.paramAsStrings(IssueFilterParameters.SEVERITIES))
+ .statuses(request.paramAsStrings(IssueFilterParameters.STATUSES))
+ .resolutions(request.paramAsStrings(IssueFilterParameters.RESOLUTIONS))
+ .resolved(request.paramAsBoolean(IssueFilterParameters.RESOLVED))
+ .components(request.paramAsStrings(IssueFilterParameters.COMPONENTS))
+ .componentRoots(request.paramAsStrings(IssueFilterParameters.COMPONENT_ROOTS))
+ .rules(stringsToRules(request.paramAsStrings(IssueFilterParameters.RULES)))
+ .actionPlans(request.paramAsStrings(IssueFilterParameters.ACTION_PLANS))
+ .reporters(request.paramAsStrings(IssueFilterParameters.REPORTERS))
+ .assignees(request.paramAsStrings(IssueFilterParameters.ASSIGNEES))
+ .languages(request.paramAsStrings(IssueFilterParameters.LANGUAGES))
+ .assigned(request.paramAsBoolean(IssueFilterParameters.ASSIGNED))
+ .planned(request.paramAsBoolean(IssueFilterParameters.PLANNED))
+ .hideRules(request.paramAsBoolean(IssueFilterParameters.HIDE_RULES))
+ .createdAt(request.paramAsDateTime(IssueFilterParameters.CREATED_AT))
+ .createdAfter(request.paramAsDateTime(IssueFilterParameters.CREATED_AFTER))
+ .createdBefore(request.paramAsDateTime(IssueFilterParameters.CREATED_BEFORE))
+ .pageSize(request.paramAsInt(IssueFilterParameters.PAGE_SIZE))
+ .pageIndex(request.paramAsInt(IssueFilterParameters.PAGE_INDEX));
+ String sort = request.param(IssueFilterParameters.SORT);
+ if (!Strings.isNullOrEmpty(sort)) {
+ builder.sort(sort);
+ builder.asc(request.paramAsBoolean(IssueFilterParameters.ASC));
+ }
+ return builder.build();
+ }
- IssueResult results = service.search(query, queryContext);
+ @Override
+ protected Result<Issue> doSearch(IssueQuery query, QueryContext context) {
+ return service.search(query, context);
+ }
- JsonWriter json = response.newJsonWriter();
- json.beginObject();
+ @Override
+ protected void doResultResponse(Request request, QueryContext context, Result<Issue> result, JsonWriter json) {
+ writeIssues(result, request.paramAsStrings(EXTRA_FIELDS_PARAM), json);
+ }
- writePaging(results, json);
- writeIssues(results, request.paramAsStrings(EXTRA_FIELDS_PARAM), json);
+ @Override
+ protected void doContextResponse(Request request, QueryContext context, Result<Issue> result, JsonWriter json) {
+
+ // Insert the projects and component name;
+ Set<RuleKey> ruleKeys = new HashSet<RuleKey>();
+ Set<String> projectKeys = new HashSet<String>();
+ Set<String> componentKeys = new HashSet<String>();
+ Set<String> actionPlanKeys = new HashSet<String>();
+ List<String> userLogins = new ArrayList<String>();
+ //
+ // DbSession session = dbClient.openSession(false);
+ for (Issue issue : result.getHits()) {
+ ruleKeys.add(issue.ruleKey());
+ projectKeys.add(issue.projectKey());
+ componentKeys.add(issue.componentKey());
+ actionPlanKeys.add(issue.actionPlanKey());
+ userLogins.add(issue.authorLogin());
+ }
- // TODO normalize component name for snippet -- Mighty change over time (file move)
- writeComponents(results, json);
- // TODO normalize project Name for snippet -- Carefull might change over time (Project Rename)
- writeProjects(results, json);
+ writeRules(json, ruleService.getByKeys(ruleKeys));
+ writeUsers(json, userFinder.findByLogins(userLogins));
+ writeActionPlans(json, actionPlanService.findByKeys(actionPlanKeys));
- // TODO Not certain that this is required (Legacy)
- // writeRules(results, json);
- // writeUsers(results, json);
- // writeActionPlans(results, json);
+ DbSession session = dbClient.openSession(false);
+ try {
+ writeProjects(json, dbClient.componentDao().getByKeys(session, projectKeys));
+ writeComponents(json, dbClient.componentDao().getByKeys(session, componentKeys));
+ } finally {
+ session.close();
+ }
- if (queryContext.isFacet()) {
- writeFacets(results, json);
- }
+ // TODO remove legacy paging. Handled by the SearchRequestHandler
+ writeLegacyPaging(context, json, result);
+ }
- json.endObject().close();
+ private void writeLegacyPaging(QueryContext context, JsonWriter json, Result<?> result) {
+ // TODO remove with stas on HTML side
+ json.prop("maxResultsReached", false);
+ json.name("paging").beginObject()
+ .prop("pageIndex", context.getPage())
+ .prop("pageSize", context.getLimit())
+ .prop("total", result.getTotal())
+ // TODO Remove as part of Front-end rework on Issue Domain
+ .prop("fTotal", i18n.formatInteger(UserSession.get().locale(), (int) result.getTotal()))
+ .prop("pages", Math.ceil(result.getTotal() / (context.getLimit() * 1.0)))
+ .endObject();
}
- private void writeFacets(Result<?> results, JsonWriter json) {
- json.name("facets").beginArray();
- for (Map.Entry<String, Collection<FacetValue>> facet : results.getFacets().entrySet()) {
- json.beginObject();
- json.prop("property", facet.getKey());
- json.name("values").beginArray();
- for (FacetValue facetValue : facet.getValue()) {
- json.beginObject();
- json.prop("val", facetValue.getKey());
- json.prop("count", facetValue.getValue());
- json.endObject();
- }
- json.endArray().endObject();
+ // TODO change to use the RuleMapper
+ private void writeRules(JsonWriter json, Collection<Rule> rules) {
+ json.name("rules").beginArray();
+ for (Rule rule : rules) {
+ json.beginObject()
+ .prop("key", rule.key().toString())
+ .prop("name", rule.name())
+ .prop("desc", rule.htmlDescription())
+ .prop("status", rule.status().toString())
+ .endObject();
}
json.endArray();
}
- private void writePaging(IssueQueryResult result, JsonWriter json) {
- json.prop("maxResultsReached", result.maxResultsReached());
- json.name("paging").beginObject()
- .prop("pageIndex", result.paging().pageIndex())
- .prop("pageSize", result.paging().pageSize())
- .prop("total", result.paging().total())
- // TODO Remove as part of Front-end rework on Issue Domain
- .prop("fTotal", i18n.formatInteger(UserSession.get().locale(), result.paging().total()))
- .prop("pages", result.paging().pages())
- .endObject();
- }
+ private void writeIssues(Result<Issue> result, @Nullable List<String> extraFields, JsonWriter json) {
+ json.name("issues").beginArray();
- private void writeIssues(IssueQueryResult result, @Nullable List<String> extraFields, JsonWriter json) {
- json.name("issues").beginArray();
-
- for (Issue issue : result.issues()) {
- json.beginObject();
+ for (Issue issue : result.getHits()) {
+ json.beginObject();
String actionPlanKey = issue.actionPlanKey();
Duration debt = issue.debt();
.prop("fUpdateAge", formatAgeDate(updateDate))
.prop("closeDate", isoDate(issue.closeDate()));
- writeIssueComments(result, issue, json);
+ // TODO add comments
+ // writeIssueComments(result, issue, json);
writeIssueAttributes(issue, json);
- writeIssueExtraFields(result, issue, extraFields, json);
+ // TODO Add fields
+ // writeIssueExtraFields(result, issue, extraFields, json);
json.endObject();
}
}
}
- private void writeComponents(IssueQueryResult result, JsonWriter json) {
+ private void writeComponents(JsonWriter json, List<ComponentDto> components) {
json.name("components").beginArray();
- for (Component component : result.components()) {
- ComponentDto componentDto = (ComponentDto) component;
+ for (ComponentDto component : components) {
json.beginObject()
.prop("key", component.key())
- .prop("id", componentDto.getId())
- .prop("qualifier", component.qualifier())
+ .prop("id", component.getId())
+ .prop("qualifier", component.qualifier())
.prop("name", component.name())
.prop("longName", component.longName())
.prop("path", component.path())
// On a root project, subProjectId is null but projectId is equal to itself, which make no sense.
- .prop("projectId", (componentDto.projectId() != null && componentDto.subProjectId() != null) ? componentDto.projectId() : null)
- .prop("subProjectId", componentDto.subProjectId())
+ .prop("projectId", (component.projectId() != null && component.subProjectId() != null) ? component.projectId() : null)
+ .prop("subProjectId", component.subProjectId())
.endObject();
}
json.endArray();
}
- private void writeProjects(IssueQueryResult result, JsonWriter json) {
+ private void writeProjects(JsonWriter json, List<ComponentDto> projects) {
json.name("projects").beginArray();
- for (Component project : result.projects()) {
- ComponentDto componentDto = (ComponentDto) project;
+ for (ComponentDto project : projects) {
json.beginObject()
.prop("key", project.key())
- .prop("id", componentDto.getId())
+ .prop("id", project.getId())
.prop("qualifier", project.qualifier())
.prop("name", project.name())
.prop("longName", project.longName())
json.endArray();
}
- private void writeRules(IssueQueryResult result, JsonWriter json) {
- json.name("rules").beginArray();
- for (Rule rule : result.rules()) {
- json.beginObject()
- .prop("key", rule.ruleKey().toString())
- .prop("name", rule.getName())
- .prop("desc", rule.getDescription())
- .prop("status", rule.getStatus())
- .endObject();
- }
- json.endArray();
- }
-
- private void writeUsers(IssueQueryResult result, JsonWriter json) {
+ private void writeUsers(JsonWriter json, List<User> users) {
json.name("users").beginArray();
- for (User user : result.users()) {
+ for (User user : users) {
json.beginObject()
.prop("login", user.login())
.prop("name", user.name())
json.endArray();
}
- private void writeActionPlans(IssueQueryResult result, JsonWriter json) {
- if (!result.actionPlans().isEmpty()) {
+ private void writeActionPlans(JsonWriter json, List<ActionPlan> plans) {
+ if (!plans.isEmpty()) {
json.name("actionPlans").beginArray();
- for (ActionPlan actionPlan : result.actionPlans()) {
+ for (ActionPlan actionPlan : plans) {
Date deadLine = actionPlan.deadLine();
Date updatedAt = actionPlan.updatedAt();
}
return null;
}
-
- @VisibleForTesting
- static IssueQuery createQuery(Request request) {
- IssueQuery.Builder builder = IssueQuery.builder()
- .requiredRole(UserRole.USER)
- .issueKeys(request.paramAsStrings(IssueFilterParameters.ISSUES))
- .severities(request.paramAsStrings(IssueFilterParameters.SEVERITIES))
- .statuses(request.paramAsStrings(IssueFilterParameters.STATUSES))
- .resolutions(request.paramAsStrings(IssueFilterParameters.RESOLUTIONS))
- .resolved(request.paramAsBoolean(IssueFilterParameters.RESOLVED))
- .components(request.paramAsStrings(IssueFilterParameters.COMPONENTS))
- .componentRoots(request.paramAsStrings(IssueFilterParameters.COMPONENT_ROOTS))
- .rules(stringsToRules(request.paramAsStrings(IssueFilterParameters.RULES)))
- .actionPlans(request.paramAsStrings(IssueFilterParameters.ACTION_PLANS))
- .reporters(request.paramAsStrings(IssueFilterParameters.REPORTERS))
- .assignees(request.paramAsStrings(IssueFilterParameters.ASSIGNEES))
- .languages(request.paramAsStrings(IssueFilterParameters.LANGUAGES))
- .assigned(request.paramAsBoolean(IssueFilterParameters.ASSIGNED))
- .planned(request.paramAsBoolean(IssueFilterParameters.PLANNED))
- .hideRules(request.paramAsBoolean(IssueFilterParameters.HIDE_RULES))
- .createdAt(request.paramAsDateTime(IssueFilterParameters.CREATED_AT))
- .createdAfter(request.paramAsDateTime(IssueFilterParameters.CREATED_AFTER))
- .createdBefore(request.paramAsDateTime(IssueFilterParameters.CREATED_BEFORE))
- .pageSize(request.paramAsInt(IssueFilterParameters.PAGE_SIZE))
- .pageIndex(request.paramAsInt(IssueFilterParameters.PAGE_INDEX));
- String sort = request.param(IssueFilterParameters.SORT);
- if (!Strings.isNullOrEmpty(sort)) {
- builder.sort(sort);
- builder.asc(request.paramAsBoolean(IssueFilterParameters.ASC));
- }
- return builder.build();
- }
}
import javax.annotation.CheckForNull;
+import java.util.Collection;
+import java.util.List;
import java.util.Set;
/**
return index.getByKey(key);
}
+ public List<Rule> getByKeys(Collection<RuleKey> keys) {
+ return index.getByKeys(keys);
+ }
+
public Rule getNonNullByKey(RuleKey key) {
Rule rule = index.getByKey(key);
if (rule == null) {
return null;
}
- public Collection<DOMAIN> getByKeys(Collection<KEY> keys) {
+ public List<DOMAIN> getByKeys(Collection<KEY> keys) {
List<DOMAIN> results = new ArrayList<DOMAIN>();
MultiGetRequestBuilder request = client.prepareMultiGet()
.setPreference("_local");
return this;
}
+ public int getPage() {
+ double page = new Double(getOffset() + 1) / new Double(getLimit());
+ return (int) Math.ceil(page);
+ }
+
/**
* Limit on the number of results to return. Defaults to {@link #DEFAULT_LIMIT}.
*/
public Set<String> getUserGroups() {
return userGroups;
}
-
}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.search.ws;
+
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.server.search.FacetValue;
+import org.sonar.server.search.QueryContext;
+import org.sonar.server.search.Result;
+
+import javax.annotation.CheckForNull;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+public abstract class SearchRequestHandler<QUERY, DOMAIN> implements RequestHandler {
+
+ public static final String PARAM_TEXT_QUERY = "q";
+ public static final String PARAM_PAGE = "p";
+ public static final String PARAM_PAGE_SIZE = "ps";
+ public static final String PARAM_FIELDS = "f";
+ public static final String PARAM_SORT = "s";
+ public static final String PARAM_ASCENDING = "asc";
+
+ public static final String PARAM_FACETS = "facets";
+
+ private int pageSize;
+ private int page;
+ private List<String> fields;
+
+ private final String actionName;
+
+ /**
+ * The fields to be returned in JSON response. <code>null</code> means that
+ * all the fields must be returned.
+ */
+ @CheckForNull
+ public List<String> fields() {
+ return fields;
+ }
+
+ protected SearchRequestHandler(String actionName) {
+ this.actionName = actionName;
+ }
+
+ protected abstract Result<DOMAIN> doSearch(QUERY query, QueryContext context);
+
+ protected abstract QUERY doQuery(Request request);
+
+ protected abstract void doResultResponse(Request request, QueryContext context, Result<DOMAIN> result, JsonWriter json);
+
+ protected abstract void doContextResponse(Request request, QueryContext context, Result<DOMAIN> result, JsonWriter json);
+
+ protected abstract void doDefinition(WebService.NewAction action);
+
+ public final void define(WebService.NewController controller) {
+ WebService.NewAction action = controller.createAction(this.actionName);
+
+ action
+ .createParam(PARAM_PAGE)
+ .setDescription("1-based page number")
+ .setExampleValue("42")
+ .setDefaultValue("1");
+
+ action
+ .createParam(PARAM_PAGE_SIZE)
+ .setDescription("Page size. Must be greater than 0.")
+ .setExampleValue("20")
+ .setDefaultValue(String.valueOf(QueryContext.DEFAULT_LIMIT));
+
+ action.createParam(PARAM_FACETS)
+ .setDescription("Compute predefined facets")
+ .setBooleanPossibleValues()
+ .setDefaultValue("false");
+
+ WebService.NewParam newParam = action
+ .createParam(PARAM_FIELDS)
+ .setDescription("Comma-separated list of the fields to be returned in response. All the fields are returned by default.");
+
+ // .setPossibleValues(possibleFields);
+ // if (possibleFields != null && possibleFields.size() > 1) {
+ // Iterator<String> it = possibleFields.iterator();
+ // newParam.setExampleValue(String.format("%s,%s", it.next(), it.next()));
+ // }
+
+ this.doDefinition(action);
+ }
+
+ @Override
+ public final void handle(Request request, Response response) throws Exception {
+ QueryContext context = getQueryContext(request);
+ QUERY query = doQuery(request);
+ Result<DOMAIN> result = doSearch(query, context);
+
+ JsonWriter json = response.newJsonWriter().beginObject();
+ this.writeStatistics(json, result);
+ doResultResponse(request, context, result, json);
+ doContextResponse(request, context, result, json);
+ if (context.isFacet()) {
+ writeFacets(result, json);
+ }
+ json.endObject().close();
+ }
+
+ private final QueryContext getQueryContext(Request request) {
+ return new QueryContext()
+ .addFieldsToReturn(request.paramAsStrings(PARAM_FIELDS))
+ .setFacet(request.paramAsBoolean(PARAM_FACETS))
+ .setPage(request.mandatoryParamAsInt(PARAM_PAGE),
+ request.mandatoryParamAsInt(PARAM_PAGE_SIZE));
+ }
+
+ protected void writeStatistics(JsonWriter json, Result searchResult) {
+ json.prop("total", searchResult.getTotal());
+ json.prop(PARAM_PAGE, page);
+ json.prop(PARAM_PAGE_SIZE, pageSize);
+ }
+
+ protected void writeFacets(Result<?> results, JsonWriter json) {
+ json.name("facets").beginArray();
+ for (Map.Entry<String, Collection<FacetValue>> facet : results.getFacets().entrySet()) {
+ json.beginObject();
+ json.prop("property", facet.getKey());
+ json.name("values").beginArray();
+ for (FacetValue facetValue : facet.getValue()) {
+ json.beginObject();
+ json.prop("val", facetValue.getKey());
+ json.prop("count", facetValue.getValue());
+ json.endObject();
+ }
+ json.endArray().endObject();
+ }
+ json.endArray();
+ }
+
+}
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
+import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueQuery;
import org.sonar.api.security.DefaultGroups;
import org.sonar.api.utils.DateUtils;
import org.sonar.server.component.persistence.ComponentDao;
import org.sonar.server.db.DbClient;
import org.sonar.server.issue.db.IssueDao;
-import org.sonar.server.issue.index.IssueResult;
import org.sonar.server.rule.RuleTesting;
import org.sonar.server.rule.db.RuleDao;
import org.sonar.server.search.QueryContext;
tester.get(IssueDao.class).insert(session, issue1, issue2);
session.commit();
- IssueResult result = service.search(IssueQuery.builder().build(), new QueryContext());
+ org.sonar.server.search.Result<Issue> result = service.search(IssueQuery.builder().build(), new QueryContext());
assertThat(result.getHits()).hasSize(2);
assertThat(result.getFacets()).isEmpty();
assertThat(result.getFacetKeys("actionPlan")).hasSize(2);
}
- @Test
- public void has_component_and_project() throws Exception {
- IssueDto issue1 = getIssue().setActionPlanKey("P1");
- IssueDto issue2 = getIssue().setActionPlanKey("P2");
- tester.get(IssueDao.class).insert(session, issue1, issue2);
- session.commit();
-
- IssueResult result = service.search(IssueQuery.builder().build(), new QueryContext());
- assertThat(result.projects()).hasSize(1);
- assertThat(result.components()).hasSize(1);
- }
-
private IssueDto getIssue() {
return new IssueDto()
.setIssueCreationDate(DateUtils.parseDate("2014-09-04"))
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
+import org.sonar.api.issue.Issue;
import org.sonar.api.rule.RuleKey;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.issue.db.IssueDto;
import org.sonar.core.rule.RuleDto;
import org.sonar.server.component.persistence.ComponentDao;
import org.sonar.server.db.DbClient;
-import org.sonar.server.issue.index.IssueDoc;
import org.sonar.server.issue.index.IssueIndex;
import org.sonar.server.platform.Platform;
import org.sonar.server.rule.RuleTesting;
assertThat(indexClient.get(IssueIndex.class).countAll()).isEqualTo(1);
// should find by key
- IssueDoc issueDoc = indexClient.get(IssueIndex.class).getByKey(issue.getKey());
+ Issue issueDoc = indexClient.get(IssueIndex.class).getByKey(issue.getKey());
assertThat(issueDoc).isNotNull();
// Check all normalized fields
package org.sonar.server.issue.index;
import com.google.common.collect.ImmutableList;
-import org.elasticsearch.action.search.SearchResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
+import org.sonar.api.issue.Issue;
import org.sonar.api.issue.IssueQuery;
import org.sonar.api.security.DefaultGroups;
import org.sonar.api.utils.DateUtils;
import org.sonar.server.rule.RuleTesting;
import org.sonar.server.rule.db.RuleDao;
import org.sonar.server.search.QueryContext;
+import org.sonar.server.search.Result;
import org.sonar.server.tester.ServerTester;
import org.sonar.server.user.MockUserSession;
IssueQuery.Builder query = IssueQuery.builder();
query.actionPlans(ImmutableList.of(plan1));
- SearchResponse result = index.search(query.build(), new QueryContext());
+ Result<Issue> result = index.search(query.build(), new QueryContext());
- assertThat(result.getHits().getTotalHits()).isEqualTo(1L);
+ assertThat(result.getHits()).hasSize(1);
query = IssueQuery.builder();
query.actionPlans(ImmutableList.of(plan2));
result = index.search(query.build(), new QueryContext());
- assertThat(result.getHits().getTotalHits()).isEqualTo(1L);
+ assertThat(result.getHits()).hasSize(1);
query = IssueQuery.builder();
query.actionPlans(ImmutableList.of(plan2, plan1));
result = index.search(query.build(), new QueryContext());
- assertThat(result.getHits().getTotalHits()).isEqualTo(2L);
+ assertThat(result.getHits()).hasSize(2);
}
@Test
session.commit();
IssueQuery.Builder query = IssueQuery.builder();
- SearchResponse result = index.search(query.build(), new QueryContext());
- assertThat(result.getHits().getTotalHits()).isEqualTo(2L);
+ Result<Issue> result = index.search(query.build(), new QueryContext());
+ assertThat(result.getHits()).hasSize(2);
query = IssueQuery.builder();
query.assigned(true);
result = index.search(query.build(), new QueryContext());
- assertThat(result.getHits().getTotalHits()).isEqualTo(1L);
+ assertThat(result.getHits()).hasSize(1);
}
@Test
session.commit();
IssueQuery.Builder query = IssueQuery.builder();
- SearchResponse result = index.search(query.build(), new QueryContext());
- assertThat(result.getHits().getTotalHits()).isEqualTo(3L);
+ Result<Issue> result = index.search(query.build(), new QueryContext());
+ assertThat(result.getHits()).hasSize(3);
query = IssueQuery.builder();
query.assignees(ImmutableList.of(assignee1));
result = index.search(query.build(), new QueryContext());
- assertThat(result.getHits().getTotalHits()).isEqualTo(1L);
+ assertThat(result.getHits()).hasSize(1);
query = IssueQuery.builder();
query.assignees(ImmutableList.of(assignee1, assignee2));
result = index.search(query.build(), new QueryContext());
- assertThat(result.getHits().getTotalHits()).isEqualTo(2L);
+ assertThat(result.getHits()).hasSize(2);
}
@Test
db.groupDao().insert(session, groupDto);
tester.get(PermissionFacade.class).insertGroupPermission(project2.getId(), groupDto.getName(), UserRole.USER, session);
-
db.issueAuthorizationDao().synchronizeAfter(session, new Date(0));
session.commit();
IssueQuery.Builder query = IssueQuery.builder();
MockUserSession.set().setUserGroups("sonar-users");
- assertThat(index.search(query.build(), new QueryContext()).getHits().getTotalHits()).isEqualTo(1L);
+ assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(1);
MockUserSession.set().setUserGroups("sonar-admins");
- assertThat(index.search(query.build(), new QueryContext()).getHits().getTotalHits()).isEqualTo(1L);
+ assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(1);
MockUserSession.set().setUserGroups("sonar-users", "sonar-admins");
- assertThat(index.search(query.build(), new QueryContext()).getHits().getTotalHits()).isEqualTo(2L);
+ assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(2);
MockUserSession.set().setUserGroups("another group");
- assertThat(index.search(query.build(), new QueryContext()).getHits().getTotalHits()).isEqualTo(0);
+ assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(0);
}
@Test
IssueQuery.Builder query = IssueQuery.builder();
MockUserSession.set().setLogin("john");
- assertThat(index.search(query.build(), new QueryContext()).getHits().getTotalHits()).isEqualTo(1L);
+ assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(1);
MockUserSession.set().setLogin("max");
- assertThat(index.search(query.build(), new QueryContext()).getHits().getTotalHits()).isEqualTo(1L);
+ assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(1);
MockUserSession.set().setLogin("another guy");
- assertThat(index.search(query.build(), new QueryContext()).getHits().getTotalHits()).isEqualTo(0L);
+ assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(0);
}
@Test
IssueQuery.Builder query = IssueQuery.builder();
MockUserSession.set().setLogin("john").setUserGroups("sonar-users");
- assertThat(index.search(query.build(), new QueryContext()).getHits().getTotalHits()).isEqualTo(1L);
+ assertThat(index.search(query.build(), new QueryContext()).getHits()).hasSize(1);
}
private IssueDto createIssue() {