@@ -19,8 +19,11 @@ | |||
*/ | |||
package org.sonar.db.rule; | |||
import java.util.function.Consumer; | |||
import org.sonar.db.DbTester; | |||
import static org.sonar.db.rule.RuleTesting.newRuleDto; | |||
public class RuleDbTester { | |||
private final DbTester db; | |||
@@ -34,4 +37,17 @@ public class RuleDbTester { | |||
db.commit(); | |||
return ruleDto; | |||
} | |||
public RuleDto insertRule() { | |||
return insertRule(rule -> { | |||
}); | |||
} | |||
public RuleDto insertRule(Consumer<RuleDto> populateRuleDto) { | |||
RuleDto ruleDto = newRuleDto(); | |||
populateRuleDto.accept(ruleDto); | |||
db.getDbClient().ruleDao().insert(db.getSession(), ruleDto); | |||
db.commit(); | |||
return ruleDto; | |||
} | |||
} |
@@ -54,16 +54,6 @@ public class IssueService { | |||
this.userSession = userSession; | |||
} | |||
/** | |||
* Search for all tags, whatever issue resolution or user access rights | |||
*/ | |||
public List<String> listTags(@Nullable String textQuery, int pageSize) { | |||
IssueQuery query = IssueQuery.builder() | |||
.checkAuthorization(false) | |||
.build(); | |||
return issueIndex.listTags(query, textQuery, pageSize); | |||
} | |||
public List<String> listAuthors(@Nullable String textQuery, int pageSize) { | |||
IssueQuery query = IssueQuery.builder() | |||
.checkAuthorization(false) |
@@ -587,11 +587,12 @@ public class IssueIndex extends BaseIndex { | |||
return values.isEmpty() ? null : termsQuery(field, values); | |||
} | |||
public List<String> listTags(IssueQuery query, @Nullable String textQuery, int maxNumberOfTags) { | |||
public List<String> listTags(@Nullable String textQuery, int maxNumberOfTags) { | |||
SearchRequestBuilder requestBuilder = getClient() | |||
.prepareSearch(IssueIndexDefinition.INDEX_TYPE_ISSUE, RuleIndexDefinition.INDEX_TYPE_RULE); | |||
requestBuilder.setQuery(boolQuery().must(matchAllQuery()).filter(createBoolFilter(query))); | |||
requestBuilder.setQuery(boolQuery().must(matchAllQuery()).filter(createBoolFilter( | |||
IssueQuery.builder().checkAuthorization(false).build()))); | |||
GlobalBuilder topAggreg = AggregationBuilders.global("tags"); | |||
String tagsOnIssuesSubAggregation = "tags__issues"; |
@@ -20,13 +20,17 @@ | |||
package org.sonar.server.issue.ws; | |||
import com.google.common.io.Resources; | |||
import java.util.List; | |||
import javax.annotation.Nullable; | |||
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.NewAction; | |||
import org.sonar.api.server.ws.WebService.Param; | |||
import org.sonar.api.utils.text.JsonWriter; | |||
import org.sonar.server.issue.IssueService; | |||
import org.sonar.server.issue.index.IssueIndex; | |||
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE; | |||
/** | |||
* List issue tags matching a given query. | |||
@@ -34,10 +38,10 @@ import org.sonar.server.issue.IssueService; | |||
*/ | |||
public class TagsAction implements IssuesWsAction { | |||
private final IssueService service; | |||
private final IssueIndex issueIndex; | |||
public TagsAction(IssueService service) { | |||
this.service = service; | |||
public TagsAction(IssueIndex issueIndex) { | |||
this.issueIndex = issueIndex; | |||
} | |||
@Override | |||
@@ -50,7 +54,7 @@ public class TagsAction implements IssuesWsAction { | |||
action.createParam(Param.TEXT_QUERY) | |||
.setDescription("A pattern to match tags against") | |||
.setExampleValue("misra"); | |||
action.createParam("ps") | |||
action.createParam(PAGE_SIZE) | |||
.setDescription("The size of the list to return") | |||
.setExampleValue("25") | |||
.setDefaultValue("10"); | |||
@@ -60,11 +64,20 @@ public class TagsAction implements IssuesWsAction { | |||
public void handle(Request request, Response response) throws Exception { | |||
String query = request.param(Param.TEXT_QUERY); | |||
int pageSize = request.mandatoryParamAsInt("ps"); | |||
JsonWriter json = response.newJsonWriter().beginObject().name("tags").beginArray(); | |||
for (String tag: service.listTags(query, pageSize)) { | |||
json.value(tag); | |||
List<String> tags = listTags(query, pageSize); | |||
writeTags(response, tags); | |||
} | |||
private List<String> listTags(@Nullable String textQuery, int pageSize) { | |||
return issueIndex.listTags(textQuery, pageSize); | |||
} | |||
private static void writeTags(Response response, List<String> tags) { | |||
try (JsonWriter json = response.newJsonWriter()) { | |||
json.beginObject().name("tags").beginArray(); | |||
tags.forEach(json::value); | |||
json.endArray().endObject(); | |||
} | |||
json.endArray().endObject().close(); | |||
} | |||
} |
@@ -88,25 +88,6 @@ public class IssueServiceMediumTest { | |||
session.close(); | |||
} | |||
@Test | |||
public void list_tags() { | |||
RuleDto rule = newRule(); | |||
ComponentDto project = newProject(); | |||
ComponentDto file = newFile(project); | |||
saveIssue(IssueTesting.newDto(rule, file, project).setTags(ImmutableSet.of("convention", "java8", "bug"))); | |||
saveIssue(IssueTesting.newDto(rule, file, project).setTags(ImmutableSet.of("convention", "bug"))); | |||
saveIssue(IssueTesting.newDto(rule, file, project)); | |||
saveIssue(IssueTesting.newDto(rule, file, project).setTags(ImmutableSet.of("convention"))); | |||
assertThat(service.listTags(null, 5)).containsOnly("convention", "java8", "bug" /* from issues */, "systag1", "systag2" /* from rules */); | |||
assertThat(service.listTags(null, 2)).containsOnly("bug", "convention"); | |||
assertThat(service.listTags("vent", 5)).containsOnly("convention"); | |||
assertThat(service.listTags("sys", 5)).containsOnly("systag1", "systag2"); | |||
assertThat(service.listTags(null, 1)).containsOnly("bug"); | |||
assertThat(service.listTags(null, Integer.MAX_VALUE)).containsOnly("convention", "java8", "bug", "systag1", "systag2", "tag1", "tag2"); | |||
assertThat(service.listTags("invalidRegexp[", 5)).isEmpty(); | |||
} | |||
@Test | |||
public void set_tags() { | |||
RuleDto rule = newRule(); |
@@ -1,86 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2017 SonarSource SA | |||
* mailto:info 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.Lists; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import org.junit.runner.RunWith; | |||
import org.mockito.Mock; | |||
import org.mockito.runners.MockitoJUnitRunner; | |||
import org.sonar.api.server.ws.WebService.Action; | |||
import org.sonar.api.server.ws.WebService.Param; | |||
import org.sonar.server.issue.IssueService; | |||
import org.sonar.server.ws.WsTester; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.mockito.Mockito.verify; | |||
import static org.mockito.Mockito.when; | |||
@RunWith(MockitoJUnitRunner.class) | |||
public class IssueTagsActionTest { | |||
@Mock | |||
private IssueService service; | |||
private TagsAction tagsAction; | |||
private WsTester tester; | |||
@Before | |||
public void setUp() { | |||
tagsAction = new TagsAction(service); | |||
tester = new WsTester(new IssuesWs(tagsAction)); | |||
} | |||
@Test | |||
public void should_define() { | |||
Action action = tester.controller("api/issues").action("tags"); | |||
assertThat(action.description()).isNotEmpty(); | |||
assertThat(action.responseExampleAsString()).isNotEmpty(); | |||
assertThat(action.isPost()).isFalse(); | |||
assertThat(action.isInternal()).isFalse(); | |||
assertThat(action.handler()).isEqualTo(tagsAction); | |||
assertThat(action.params()).hasSize(2); | |||
Param query = action.param("q"); | |||
assertThat(query.isRequired()).isFalse(); | |||
assertThat(query.description()).isNotEmpty(); | |||
assertThat(query.exampleValue()).isNotEmpty(); | |||
Param pageSize = action.param("ps"); | |||
assertThat(pageSize.isRequired()).isFalse(); | |||
assertThat(pageSize.defaultValue()).isEqualTo("10"); | |||
assertThat(pageSize.description()).isNotEmpty(); | |||
assertThat(pageSize.exampleValue()).isNotEmpty(); | |||
} | |||
@Test | |||
public void should_return_empty_list() throws Exception { | |||
tester.newGetRequest("api/issues", "tags").execute().assertJson("{\"tags\":[]}"); | |||
} | |||
@Test | |||
public void should_return_tag_list() throws Exception { | |||
when(service.listTags("polop", 5)).thenReturn(Lists.newArrayList("tag1", "tag2", "tag3", "tag4", "tag5")); | |||
tester.newGetRequest("api/issues", "tags").setParam("q", "polop").setParam("ps", "5").execute() | |||
.assertJson("{\"tags\":[\"tag1\", \"tag2\", \"tag3\", \"tag4\", \"tag5\"]}"); | |||
verify(service).listTags("polop", 5); | |||
} | |||
} |
@@ -0,0 +1,182 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2017 SonarSource SA | |||
* mailto:info 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.ImmutableSet; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonar.api.config.MapSettings; | |||
import org.sonar.api.server.ws.WebService.Action; | |||
import org.sonar.api.server.ws.WebService.Param; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.issue.IssueDto; | |||
import org.sonar.db.issue.IssueTesting; | |||
import org.sonar.db.rule.RuleDto; | |||
import org.sonar.server.es.EsTester; | |||
import org.sonar.server.issue.index.IssueIndex; | |||
import org.sonar.server.issue.index.IssueIndexDefinition; | |||
import org.sonar.server.issue.index.IssueIndexer; | |||
import org.sonar.server.issue.index.IssueIteratorFactory; | |||
import org.sonar.server.permission.index.AuthorizationTypeSupport; | |||
import org.sonar.server.rule.index.RuleIndexDefinition; | |||
import org.sonar.server.rule.index.RuleIndexer; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import org.sonar.server.ws.WsActionTester; | |||
import static java.util.Arrays.asList; | |||
import static java.util.Collections.emptySet; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.api.web.UserRole.USER; | |||
import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
import static org.sonar.db.rule.RuleTesting.newRuleDto; | |||
import static org.sonar.test.JsonAssert.assertJson; | |||
public class TagsActionTest { | |||
@Rule | |||
public UserSessionRule userSession = UserSessionRule.standalone(); | |||
@Rule | |||
public DbTester db = DbTester.create(); | |||
@Rule | |||
public EsTester es = new EsTester(new IssueIndexDefinition(new MapSettings()), new RuleIndexDefinition(new MapSettings())); | |||
private IssueIndexer issueIndexer = new IssueIndexer(es.client(), new IssueIteratorFactory(db.getDbClient())); | |||
private RuleIndexer ruleIndexer = new RuleIndexer(System2.INSTANCE, db.getDbClient(), es.client()); | |||
private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new AuthorizationTypeSupport(userSession)); | |||
private WsActionTester tester = new WsActionTester(new TagsAction(issueIndex)); | |||
@Test | |||
public void return_tags_from_issues() throws Exception { | |||
insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag1", "tag2"); | |||
insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag3", "tag4", "tag5"); | |||
issueIndexer.indexOnStartup(null); | |||
String result = tester.newRequest().execute().getInput(); | |||
assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag2\", \"tag3\", \"tag4\", \"tag5\"]}"); | |||
} | |||
@Test | |||
public void return_tags_from_rules() throws Exception { | |||
db.rules().insertRule(rule -> rule.setSystemTags(ImmutableSet.of("tag1")).setTags(ImmutableSet.of("tag2"))); | |||
db.rules().insertRule(rule -> rule.setSystemTags(ImmutableSet.of("tag3")).setTags(ImmutableSet.of("tag4", "tag5"))); | |||
ruleIndexer.index(); | |||
String result = tester.newRequest().execute().getInput(); | |||
assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag2\", \"tag3\", \"tag4\", \"tag5\"]}"); | |||
} | |||
@Test | |||
public void return_tags_from_issue_and_rule_tags() throws Exception { | |||
insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag1", "tag2"); | |||
insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag3", "tag4", "tag5"); | |||
issueIndexer.indexOnStartup(null); | |||
db.rules().insertRule(rule -> rule.setSystemTags(ImmutableSet.of("tag6")).setTags(ImmutableSet.of("tag7"))); | |||
ruleIndexer.index(); | |||
String result = tester.newRequest().execute().getInput(); | |||
assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag2\", \"tag3\", \"tag4\", \"tag5\", \"tag6\", \"tag7\"]}"); | |||
} | |||
@Test | |||
public void return_limited_size() throws Exception { | |||
insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag1", "tag2"); | |||
insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag3", "tag4", "tag5"); | |||
issueIndexer.indexOnStartup(null); | |||
String result = tester.newRequest().setParam("ps", "2").execute().getInput(); | |||
assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag2\"]}"); | |||
} | |||
@Test | |||
public void return_tags_matching_query() throws Exception { | |||
insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag1", "tag2"); | |||
insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag12", "tag4", "tag5"); | |||
issueIndexer.indexOnStartup(null); | |||
String result = tester.newRequest().setParam("q", "ag1").execute().getInput(); | |||
assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag12\"]}"); | |||
} | |||
@Test | |||
public void return_empty_list() throws Exception { | |||
String result = tester.newRequest().execute().getInput(); | |||
assertJson(result).isSimilarTo("{\"tags\":[]}"); | |||
} | |||
@Test | |||
public void test_example() throws Exception { | |||
insertIssueWithBrowsePermission(insertRuleWithoutTags(), "convention"); | |||
issueIndexer.indexOnStartup(null); | |||
db.rules().insertRule(rule -> rule.setSystemTags(ImmutableSet.of("cwe")).setTags(ImmutableSet.of("security"))); | |||
ruleIndexer.index(); | |||
String result = tester.newRequest().execute().getInput(); | |||
assertJson(result).isSimilarTo(tester.getDef().responseExampleAsString()); | |||
} | |||
@Test | |||
public void test_definition() { | |||
Action action = tester.getDef(); | |||
assertThat(action.description()).isNotEmpty(); | |||
assertThat(action.responseExampleAsString()).isNotEmpty(); | |||
assertThat(action.isPost()).isFalse(); | |||
assertThat(action.isInternal()).isFalse(); | |||
assertThat(action.params()).hasSize(2); | |||
Param query = action.param("q"); | |||
assertThat(query.isRequired()).isFalse(); | |||
assertThat(query.description()).isNotEmpty(); | |||
assertThat(query.exampleValue()).isNotEmpty(); | |||
Param pageSize = action.param("ps"); | |||
assertThat(pageSize.isRequired()).isFalse(); | |||
assertThat(pageSize.defaultValue()).isEqualTo("10"); | |||
assertThat(pageSize.description()).isNotEmpty(); | |||
assertThat(pageSize.exampleValue()).isNotEmpty(); | |||
} | |||
private RuleDto insertRuleWithoutTags() { | |||
RuleDto ruleDto = newRuleDto().setTags(emptySet()).setSystemTags(emptySet()); | |||
db.rules().insertRule(ruleDto); | |||
return ruleDto; | |||
} | |||
private IssueDto insertIssue(RuleDto rule, String... tags) { | |||
ComponentDto project = db.components().insertProject(); | |||
ComponentDto file = db.components().insertComponent(newFileDto(project)); | |||
IssueDto issueDto = IssueTesting.newDto(rule, file, project).setTags(asList(tags)); | |||
return db.issues().insertIssue(issueDto); | |||
} | |||
private void setUserWithBrowsePermission(IssueDto issue) { | |||
userSession.logIn("john").addProjectUuidPermissions(USER, issue.getProjectUuid()); | |||
} | |||
private IssueDto insertIssueWithBrowsePermission(RuleDto rule, String... tags) { | |||
IssueDto issue = insertIssue(rule, tags); | |||
setUserWithBrowsePermission(issue); | |||
return issue; | |||
} | |||
} |