import org.sonar.core.activity.Activity;
import org.sonar.server.activity.index.ActivityDoc;
import org.sonar.server.activity.index.ActivityNormalizer;
+import org.sonar.server.search.QueryContext;
import org.sonar.server.search.ws.BaseMapping;
-import org.sonar.server.search.ws.SearchOptions;
import java.util.Map;
});
}
- public void write(Activity activity, JsonWriter writer, SearchOptions options) {
- doWrite((ActivityDoc)activity, null, writer, options);
+ public void write(Activity activity, JsonWriter writer, QueryContext context) {
+ doWrite((ActivityDoc) activity, null, writer, context);
}
}
JsonWriter json = response.newJsonWriter().beginObject();
searchOptions.writeStatistics(json, results);
- writeLogs(results, json, searchOptions);
+ writeLogs(results, json, queryContext);
json.endObject().close();
}
- private void writeLogs(Result<Activity> result, JsonWriter json, SearchOptions options) {
+ private void writeLogs(Result<Activity> result, JsonWriter json, QueryContext context) {
json.name("logs").beginArray();
for (Activity log : result.getHits()) {
- mapping.write(log, json, options);
+ mapping.write(log, json, context);
}
json.endArray();
}
import org.sonar.server.rule.RuleParam;
import org.sonar.server.rule.index.RuleDoc;
import org.sonar.server.rule.index.RuleNormalizer;
+import org.sonar.server.search.QueryContext;
import org.sonar.server.search.ws.BaseMapping;
-import org.sonar.server.search.ws.SearchOptions;
import org.sonar.server.text.MacroInterpreter;
import javax.annotation.CheckForNull;
});
}
- public void write(Rule rule, JsonWriter json, @Nullable SearchOptions options) {
+ public void write(Rule rule, JsonWriter json, @Nullable QueryContext queryContext) {
RuleMappingContext context = new RuleMappingContext();
- if (needDebtCharacteristicNames(options)) {
+ if (needDebtCharacteristicNames(queryContext)) {
String debtCharacteristicKey = rule.debtCharacteristicKey();
if (debtCharacteristicKey != null) {
// load debt characteristics if requested
context.add(debtModel.characteristicByKey(debtCharacteristicKey));
}
}
- if (needDebtSubCharacteristicNames(options)) {
+ if (needDebtSubCharacteristicNames(queryContext)) {
String debtSubCharacteristicKey = rule.debtSubCharacteristicKey();
if (debtSubCharacteristicKey != null) {
context.add(debtModel.characteristicByKey(debtSubCharacteristicKey));
}
}
- doWrite((RuleDoc) rule, context, json, options);
+ doWrite((RuleDoc) rule, context, json, queryContext);
}
- public void write(Collection<Rule> rules, JsonWriter json, @Nullable SearchOptions options) {
+ public void write(Collection<Rule> rules, JsonWriter json, @Nullable QueryContext queryContext) {
if (!rules.isEmpty()) {
RuleMappingContext context = new RuleMappingContext();
- if (needDebtCharacteristicNames(options) || needDebtSubCharacteristicNames(options)) {
+ if (needDebtCharacteristicNames(queryContext) || needDebtSubCharacteristicNames(queryContext)) {
// load all debt characteristics
context.addAll(debtModel.allCharacteristics());
}
for (Rule rule : rules) {
- doWrite((RuleDoc) rule, context, json, options);
+ doWrite((RuleDoc) rule, context, json, queryContext);
}
}
}
- private boolean needDebtCharacteristicNames(@Nullable SearchOptions options) {
- return options == null || options.hasField("debtCharName");
+ private boolean needDebtCharacteristicNames(@Nullable QueryContext context) {
+ return context == null || context.getFieldsToReturn().contains("debtCharName");
}
- private boolean needDebtSubCharacteristicNames(@Nullable SearchOptions options) {
- return options == null || options.hasField("debtSubCharName");
+ private boolean needDebtSubCharacteristicNames(@Nullable QueryContext context) {
+ return context == null || context.getFieldsToReturn().contains("debtSubCharName");
}
private static class CharacteristicNameMapper extends IndexMapper<RuleDoc, RuleMappingContext> {
package org.sonar.server.rule.ws;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
import com.google.common.io.Resources;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rule.Severity;
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.qualityprofile.ActiveRule;
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 javax.annotation.CheckForNull;
import java.util.Arrays;
import java.util.Collection;
/**
* @since 4.4
*/
-public class SearchAction implements RequestHandler {
+public class SearchAction extends SearchRequestHandler<RuleQuery, Rule> {
public static final String PARAM_REPOSITORIES = "repositories";
public static final String PARAM_KEY = "rule_key";
private final RuleMapping mapping;
public SearchAction(RuleService service, ActiveRuleCompleter activeRuleCompleter, RuleMapping mapping) {
+ super(SEARCH_ACTION);
this.ruleService = service;
this.activeRuleCompleter = activeRuleCompleter;
this.mapping = mapping;
}
- void define(WebService.NewController controller) {
- WebService.NewAction action = controller
- .createAction(SEARCH_ACTION)
- .setDescription("Search for a collection of relevant rules matching a specified query")
+ @Override
+ protected void doDefinition(WebService.NewAction action) {
+ action.setDescription("Search for a collection of relevant rules matching a specified query")
.setResponseExample(Resources.getResource(getClass(), "example-search.json"))
.setSince("4.4")
.setHandler(this);
- // Generic search parameters
- SearchOptions.defineFieldsParam(action,
- ImmutableList.<String>builder().addAll(mapping.supportedFields()).add("actives").build());
- SearchOptions.definePageParams(action);
-
// Rule-specific search parameters
defineRuleSearchParameters(action);
+ }
- // Other parameters
- action.createParam(PARAM_FACETS)
- .setDescription("Compute predefined facets")
- .setBooleanPossibleValues()
- .setDefaultValue("false");
+ @Override
+ @CheckForNull
+ protected Collection<String> possibleFacets() {
+ return Arrays.asList(new String[] {
+ "languages",
+ "repositories",
+ "tags",
+ "characteristics",
+ "severities",
+ "statuses",
+ "true"
+ });
}
/**
.setDefaultValue(true);
}
- @Override
- public void handle(Request request, Response response) {
- RuleQuery query = createRuleQuery(ruleService.newRuleQuery(), request);
- SearchOptions searchOptions = SearchOptions.create(request);
- QueryContext queryContext = mapping.newQueryOptions(searchOptions);
- Boolean facets = request.paramAsBoolean(PARAM_FACETS);
- if (facets != null && facets) {
- queryContext.addFacets(Arrays.asList("languages", "repositories", "tags"));
- }
-
- Result<Rule> results = ruleService.search(query, queryContext);
-
- JsonWriter json = response.newJsonWriter().beginObject();
- searchOptions.writeStatistics(json, results);
- writeRules(results, json, searchOptions);
- if (searchOptions.hasField("actives")) {
- activeRuleCompleter.completeSearch(query, results.getHits(), json);
- }
- if (queryContext.isFacet()) {
- writeFacets(results, json);
- }
- json.endObject().close();
- }
-
public static RuleQuery createRuleQuery(RuleQuery query, Request request) {
query.setQueryText(request.param(SearchOptions.PARAM_TEXT_QUERY));
query.setSeverities(request.paramAsStrings(PARAM_SEVERITIES));
return query;
}
- private void writeRules(Result<Rule> result, JsonWriter json, SearchOptions options) {
+ private void writeRules(Result<Rule> result, JsonWriter json, QueryContext context) {
json.name("rules").beginArray();
for (Rule rule : result.getHits()) {
- mapping.write(rule, json, options);
+ mapping.write(rule, json, context);
}
json.endArray();
}
}
json.endArray();
}
+
+ @Override
+ protected QueryContext getQueryContext(Request request) {
+ // TODO Get rid of this horrible hack: fields on request are not the same as fields for ES search ! 1/2
+ return mapping.newQueryOptions(SearchOptions.create(request));
+ }
+
+ @Override
+ protected Result<Rule> doSearch(RuleQuery query, QueryContext context) {
+ return ruleService.search(query, context);
+ }
+
+ @Override
+ protected RuleQuery doQuery(Request request) {
+ return createRuleQuery(ruleService.newRuleQuery(), request);
+ }
+
+ @Override
+ protected void doContextResponse(Request request, QueryContext context, Result<Rule> result, JsonWriter json) {
+ // TODO Get rid of this horrible hack: fields on request are not the same as fields for ES search ! 2/2
+ QueryContext contextForResponse = super.getQueryContext(request);
+ writeRules(result, json, contextForResponse);
+ if (contextForResponse.getFieldsToReturn().contains("actives")) {
+ activeRuleCompleter.completeSearch(doQuery(request), result.getHits(), json);
+ }
+ if (contextForResponse.isFacet()) {
+ writeFacets(result, json);
+ }
+ }
+
+ @Override
+ protected Collection<String> possibleFields() {
+ Builder<String> builder = ImmutableList.<String>builder();
+ if (mapping != null) {
+ builder.addAll(mapping.supportedFields());
+ }
+ return builder.add("actives").build();
+ }
}
/**
* Write only requested document fields
*/
- protected void doWrite(DOC doc, @Nullable CTX context, JsonWriter json, @Nullable SearchOptions options) {
+ protected void doWrite(DOC doc, @Nullable CTX context, JsonWriter json, @Nullable QueryContext queryContext) {
json.beginObject();
json.prop("key", doc.keyField());
- if (options == null || options.fields() == null) {
+ if (queryContext == null || queryContext.getFieldsToReturn().isEmpty()) {
// return all fields
for (Mapper mapper : mappers.values()) {
mapper.write(json, doc, context);
}
} else {
- for (String optionField : options.fields()) {
+ for (String optionField : queryContext.getFieldsToReturn()) {
for (Mapper mapper : mappers.get(optionField)) {
mapper.write(json, doc, context);
}
json.endObject().close();
}
- private QueryContext getQueryContext(Request request) {
+ protected QueryContext getQueryContext(Request request) {
int pageSize = request.mandatoryParamAsInt(PARAM_PAGE_SIZE);
QueryContext queryContext = new QueryContext().addFieldsToReturn(request.paramAsStrings(PARAM_FIELDS));
List<String> facets = request.paramAsStrings(PARAM_FACETS);
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.qualityprofile.db.QualityProfileDto;
import org.sonar.server.qualityprofile.QProfileLoader;
-import org.sonar.server.qualityprofile.QProfileService;
import org.sonar.server.qualityprofile.QProfileTesting;
import org.sonar.server.rule.RuleRepositories;
import org.sonar.server.user.MockUserSession;
public void should_generate_app_init_info() throws Exception {
AppAction app = new AppAction(languages, ruleRepositories, i18n, debtModel, profileLoader);
WsTester tester = new WsTester(new RulesWebService(
- mock(SearchAction.class), mock(ShowAction.class), mock(TagsAction.class), mock(CreateAction.class),
+ new SearchAction(null, null, null), mock(ShowAction.class), mock(TagsAction.class), mock(CreateAction.class),
app, mock(UpdateAction.class), mock(DeleteAction.class)));
MockUserSession.set().setGlobalPermissions(GlobalPermissions.QUALITY_PROFILE_ADMIN);
@Before
public void setUp() throws Exception {
- tester = new WsTester(new RulesWebService(mock(SearchAction.class), mock(ShowAction.class), mock(TagsAction.class), mock(CreateAction.class), mock(AppAction.class),
+ tester = new WsTester(new RulesWebService(new SearchAction(null, null, null), mock(ShowAction.class), mock(TagsAction.class), mock(CreateAction.class), mock(AppAction.class),
mock(UpdateAction.class), new DeleteAction(ruleService)));
}
public void search_no_rules() throws Exception {
MockUserSession.set();
WsTester.TestRequest request = tester.wsTester().newGetRequest(API_ENDPOINT, API_SEARCH_METHOD);
-
+ request.setParam(SearchOptions.PARAM_FIELDS, "actives");
WsTester.Result result = request.execute();
result.assertJson(this.getClass(), "search_no_rules.json");
MockUserSession.set();
WsTester.TestRequest request = tester.wsTester().newGetRequest(API_ENDPOINT, API_SEARCH_METHOD);
request.setParam(SearchAction.PARAM_KEY, RuleTesting.XOO_X1.toString());
- request.setParam(SearchOptions.PARAM_FIELDS, "");
+ request.setParam(SearchOptions.PARAM_FIELDS, "actives");
WsTester.Result result = request.execute();
- result.assertJson("{\"total\":1,\"p\":1,\"ps\":10,\"rules\":[{\"key\":\"xoo:x1\"}]}");
+ result.assertJson("{\"total\":1,\"p\":1,\"ps\":100,\"rules\":[{\"key\":\"xoo:x1\"}]}", false);
request = tester.wsTester().newGetRequest(API_ENDPOINT, API_SEARCH_METHOD);
request.setParam(SearchAction.PARAM_KEY, RuleKey.of("xoo", "unknown").toString());
+ request.setParam(SearchOptions.PARAM_FIELDS, "actives");
result = request.execute();
- result.assertJson("{\"total\":0,\"p\":1,\"ps\":10,\"rules\":[],\"actives\":{}}");
+ result.assertJson("{\"total\":0,\"p\":1,\"ps\":100,\"rules\":[],\"actives\":{}}", false);
}
request.setParam(SearchOptions.PARAM_FIELDS, "");
WsTester.Result result = request.execute();
- result.assertJson(this.getClass(), "search_active_rules.json");
+ result.assertJson(this.getClass(), "search_active_rules.json", false);
}
@Test
request.setParam(SearchAction.PARAM_QPROFILE, profile2.getKey());
request.setParam(SearchOptions.PARAM_FIELDS, "");
WsTester.Result result = request.execute();
- result.assertJson(this.getClass(), "search_profile_active_rules.json");
+ result.assertJson(this.getClass(), "search_profile_active_rules.json", false);
}
@Test
request.setParam(SearchOptions.PARAM_ASCENDING, "true");
WsTester.Result result = request.execute();
- result.assertJson("{\"total\":3,\"p\":1,\"ps\":10,\"rules\":[{\"key\":\"xoo:x2\"},{\"key\":\"xoo:x1\"},{\"key\":\"xoo:x3\"}]}");
+ result.assertJson("{\"total\":3,\"p\":1,\"ps\":100,\"rules\":[{\"key\":\"xoo:x2\"},{\"key\":\"xoo:x1\"},{\"key\":\"xoo:x3\"}]}", false);
// 2. Sort Name DESC
request = tester.wsTester().newGetRequest(API_ENDPOINT, API_SEARCH_METHOD);
request.setParam(SearchOptions.PARAM_ASCENDING, "false");
result = request.execute();
- result.assertJson("{\"total\":3,\"p\":1,\"ps\":10,\"rules\":[{\"key\":\"xoo:x3\"},{\"key\":\"xoo:x1\"},{\"key\":\"xoo:x2\"}]}");
+ result.assertJson("{\"total\":3,\"p\":1,\"ps\":100,\"rules\":[{\"key\":\"xoo:x3\"},{\"key\":\"xoo:x1\"},{\"key\":\"xoo:x2\"}]}", false);
}
request.setParam(SearchAction.PARAM_AVAILABLE_SINCE, DateUtils.formatDate(since));
request.setParam(SearchOptions.PARAM_SORT, RuleNormalizer.RuleField.KEY.field());
WsTester.Result result = request.execute();
- result.assertJson("{\"total\":2,\"p\":1,\"ps\":10,\"rules\":[{\"key\":\"xoo:x1\"},{\"key\":\"xoo:x2\"}]}");
+ result.assertJson("{\"total\":2,\"p\":1,\"ps\":100,\"rules\":[{\"key\":\"xoo:x1\"},{\"key\":\"xoo:x2\"}]}", false);
Calendar c = Calendar.getInstance();
c.setTime(since);
request.setParam(SearchOptions.PARAM_FIELDS, "");
request.setParam(SearchAction.PARAM_AVAILABLE_SINCE, DateUtils.formatDate(c.getTime()));
result = request.execute();
- result.assertJson("{\"total\":0,\"p\":1,\"ps\":10,\"rules\":[]}");
+ result.assertJson("{\"total\":0,\"p\":1,\"ps\":100,\"rules\":[]}");
}
private ActiveRuleDto newActiveRule(QualityProfileDto profile, RuleDto rule) {
}
public Result assertJson(String expectedJson) throws Exception {
+ return assertJson(expectedJson, true);
+ }
+
+ public Result assertJson(String expectedJson, boolean strict) throws Exception {
String json = outputAsString();
- JSONAssert.assertEquals(expectedJson, json, true);
+ JSONAssert.assertEquals(expectedJson, json, strict);
return this;
}
-{"total": 1, "p": 1, "ps": 10, "rules": [
+{"total": 1, "p": 1, "ps": 100, "rules": [
{
"key": "xoo:x1",
"sysTags": ["tag1"],
{
- "total": 1, "p": 1, "ps": 10,
+ "total": 1, "p": 1, "ps": 100,
"rules": [
{
"key": "xoo:x1",
{
- "total": 2, "p": 1, "ps": 10,
+ "total": 2, "p": 1, "ps": 100,
"rules": [
{
"key": "xoo:x2",
"lang": "xoo",
"params": []
}
- ],
- "actives": {}}
+ ]}
{
- "total": 1, "p": 1, "ps": 10,
+ "total": 1, "p": 1, "ps": 100,
"rules": [
{
"key": "xoo:x1"
-{"total": 1, "p": 1, "ps": 10, "rules": [
+{"total": 1, "p": 1, "ps": 100, "rules": [
{
"key": "xoo:x1",
"params": [
-{"total": 1, "p": 1, "ps": 10, "rules": [
+{"total": 1, "p": 1, "ps": 100, "rules": [
{
"key": "xoo:x1",
"debtChar": "RELIABILITY",
-{"total": 1, "p": 1, "ps": 10, "rules": [
+{"total": 1, "p": 1, "ps": 100, "rules": [
{
"key": "xoo:x1",
"debtChar": "RELIABILITY",
-{"total": 1, "p": 1, "ps": 10, "rules": [
+{"total": 1, "p": 1, "ps": 100, "rules": [
{
"key": "xoo:x1",
"debtChar": "RELIABILITY",
-{"total": 1, "p": 1, "ps": 10, "rules": [
+{"total": 1, "p": 1, "ps": 100, "rules": [
{
"key": "xoo:x1",
"debtChar": "RELIABILITY",
{
"total": 0,
"p": 1,
- "ps": 10,
+ "ps": 100,
"rules": [],
"actives": {}
}
{
"total": 1,
"p": 1,
- "ps": 10,
+ "ps": 100,
"rules": [
{
"key": "xoo:x1"
{
- "total": 1, "p": 1, "ps": 10,
+ "total": 1, "p": 1, "ps": 100,
"rules": [
{
"key": "xoo:x2",
{
- "total": 1, "p": 1, "ps": 10,
+ "total": 1, "p": 1, "ps": 100,
"rules": [
{
"key": "xoo:x1",