Browse Source

SONAR-6034 Add filter on authors and developers

tags/5.1-RC1
Jean-Baptiste Lievremont 9 years ago
parent
commit
7a15c4d106
16 changed files with 247 additions and 18 deletions
  1. 8
    0
      server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java
  2. 12
    0
      server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java
  3. 20
    10
      server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java
  4. 2
    1
      server/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterParameters.java
  5. 6
    3
      server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
  6. 4
    0
      server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
  7. 18
    1
      server/sonar-server/src/test/java/org/sonar/server/component/ComponentTesting.java
  8. 5
    1
      server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryServiceTest.java
  9. 31
    0
      server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java
  10. 48
    0
      server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java
  11. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java
  12. 28
    0
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_authors.json
  13. 21
    0
      server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_developer.json
  14. 25
    1
      sonar-core/src/main/java/org/sonar/core/user/AuthorDao.java
  15. 7
    0
      sonar-core/src/main/java/org/sonar/core/user/AuthorMapper.java
  16. 11
    0
      sonar-core/src/main/resources/org/sonar/core/user/AuthorMapper.xml

+ 8
- 0
server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java View File

@@ -19,6 +19,8 @@
*/
package org.sonar.server.db;

import org.sonar.core.user.AuthorDao;

import org.sonar.api.ServerComponent;
import org.sonar.core.issue.db.ActionPlanDao;
import org.sonar.core.persistence.DaoComponent;
@@ -83,6 +85,7 @@ public class DbClient implements ServerComponent {
private final WidgetDao widgetDao;
private final WidgetPropertyDao widgetPropertyDao;
private final FileSourceDao fileSourceDao;
private final AuthorDao authorDao;

public DbClient(Database db, MyBatis myBatis, DaoComponent... daoComponents) {
this.db = db;
@@ -114,6 +117,7 @@ public class DbClient implements ServerComponent {
widgetDao = getDao(map, WidgetDao.class);
widgetPropertyDao = getDao(map, WidgetPropertyDao.class);
fileSourceDao = getDao(map, FileSourceDao.class);
authorDao = getDao(map, AuthorDao.class);
}

public Database database() {
@@ -212,6 +216,10 @@ public class DbClient implements ServerComponent {
return fileSourceDao;
}

public AuthorDao authorDao() {
return authorDao;
}

private <K> K getDao(Map<Class, DaoComponent> map, Class<K> clazz) {
return (K) map.get(clazz);
}

+ 12
- 0
server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java View File

@@ -68,6 +68,7 @@ public class IssueQuery {
private final Collection<String> actionPlans;
private final Collection<String> reporters;
private final Collection<String> assignees;
private final Collection<String> authors;
private final Collection<String> languages;
private final Collection<String> tags;
private final Boolean onComponentOnly;
@@ -99,6 +100,7 @@ public class IssueQuery {
this.actionPlans = defaultCollection(builder.actionPlans);
this.reporters = defaultCollection(builder.reporters);
this.assignees = defaultCollection(builder.assignees);
this.authors = defaultCollection(builder.authors);
this.languages = defaultCollection(builder.languages);
this.tags = defaultCollection(builder.tags);
this.onComponentOnly = builder.onComponentOnly;
@@ -175,6 +177,10 @@ public class IssueQuery {
return assignees;
}

public Collection<String> authors() {
return authors;
}

public Collection<String> languages() {
return languages;
}
@@ -271,6 +277,7 @@ public class IssueQuery {
private Collection<String> actionPlans;
private Collection<String> reporters;
private Collection<String> assignees;
private Collection<String> authors;
private Collection<String> languages;
private Collection<String> tags;
private Boolean onComponentOnly = false;
@@ -364,6 +371,11 @@ public class IssueQuery {
return this;
}

public Builder authors(@Nullable Collection<String> l) {
this.authors = l;
return this;
}

public Builder languages(@Nullable Collection<String> l) {
this.languages = l;
return this;

+ 20
- 10
server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java View File

@@ -37,6 +37,7 @@ import org.sonar.api.server.ws.Request;
import org.sonar.api.web.UserRole;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.user.AuthorDao;
import org.sonar.server.component.ComponentService;
import org.sonar.server.db.DbClient;
import org.sonar.server.issue.IssueQuery.Builder;
@@ -63,10 +64,12 @@ public class IssueQueryService implements ServerComponent {
private static final String UNKNOWN = "<UNKNOWN>";
private final DbClient dbClient;
private final ComponentService componentService;
private final AuthorDao authorDao;

public IssueQueryService(DbClient dbClient, ComponentService componentService) {
public IssueQueryService(DbClient dbClient, ComponentService componentService, AuthorDao authorDao) {
this.dbClient = dbClient;
this.componentService = componentService;
this.authorDao = authorDao;
}

public IssueQuery createFromMap(Map<String, Object> params) {
@@ -108,7 +111,8 @@ public class IssueQueryService implements ServerComponent {
),
RubyUtils.toStrings(params.get(IssueFilterParameters.MODULE_UUIDS)),
RubyUtils.toStrings(params.get(IssueFilterParameters.DIRECTORIES)),
RubyUtils.toStrings(params.get(IssueFilterParameters.FILE_UUIDS)));
RubyUtils.toStrings(params.get(IssueFilterParameters.FILE_UUIDS)),
RubyUtils.toStrings(params.get(IssueFilterParameters.AUTHORS)));

String sort = (String) params.get(IssueFilterParameters.SORT);
if (!Strings.isNullOrEmpty(sort)) {
@@ -158,7 +162,8 @@ public class IssueQueryService implements ServerComponent {
request.paramAsStrings(IssueFilterParameters.PROJECT_UUIDS), request.paramAsStrings(IssueFilterParameters.PROJECT_KEYS),
request.paramAsStrings(IssueFilterParameters.MODULE_UUIDS),
request.paramAsStrings(IssueFilterParameters.DIRECTORIES),
request.paramAsStrings(IssueFilterParameters.FILE_UUIDS));
request.paramAsStrings(IssueFilterParameters.FILE_UUIDS),
request.paramAsStrings(IssueFilterParameters.AUTHORS));

String sort = request.param(SearchRequestHandler.PARAM_SORT);
if (!Strings.isNullOrEmpty(sort)) {
@@ -187,7 +192,8 @@ public class IssueQueryService implements ServerComponent {
@Nullable Collection<String> projectUuids, @Nullable Collection<String> projects,
@Nullable Collection<String> moduleUuids,
@Nullable Collection<String> directories,
@Nullable Collection<String> fileUuids) {
@Nullable Collection<String> fileUuids,
@Nullable Collection<String> authors) {

Set<String> allComponentUuids = Sets.newHashSet();
boolean effectiveOnComponentOnly = mergeComponentParameters(session, onComponentOnly,
@@ -202,7 +208,7 @@ public class IssueQueryService implements ServerComponent {

if (allComponentUuids.isEmpty()) {
builder.setContextualized(false);
addComponentsBelowView(builder, session, projects, projectUuids, moduleUuids, directories, fileUuids);
addComponentsBelowView(builder, session, authors, projects, projectUuids, moduleUuids, directories, fileUuids);
} else {
if (effectiveOnComponentOnly) {
builder.setContextualized(false);
@@ -235,10 +241,11 @@ public class IssueQueryService implements ServerComponent {
filteredViewUuids.add(UNKNOWN);
}
builder.viewUuids(filteredViewUuids);
addComponentsBelowView(builder, session, projects, projectUuids, moduleUuids, directories, fileUuids);
} else if ("DEV".equals(uniqueQualifier)) { // XXX No constant !!!
// TODO Get SCM accounts from dev, then search by author
// TODO addComponentsBelowView(projects, projectUuids, moduleUuids, directories, fileUuids);
addComponentsBelowView(builder, session, authors, projects, projectUuids, moduleUuids, directories, fileUuids);
} else if ("DEV".equals(uniqueQualifier)) {
// XXX No constant for developer !!!
Collection<String> actualAuthors = authors == null ? authorDao.selectScmAccountsByDeveloperUuids(allComponentUuids) : authors;
addComponentsBelowView(builder, session, actualAuthors, projects, projectUuids, moduleUuids, directories, fileUuids);
} else if (Qualifiers.PROJECT.equals(uniqueQualifier)) {
builder.projectUuids(allComponentUuids);
addComponentsBelowModule(builder, moduleUuids, directories, fileUuids);
@@ -300,9 +307,12 @@ public class IssueQueryService implements ServerComponent {
return effectiveOnComponentOnly;
}

private void addComponentsBelowView(Builder builder, DbSession session,
private void addComponentsBelowView(Builder builder, DbSession session, @Nullable Collection<String> authors,
@Nullable Collection<String> projects, @Nullable Collection<String> projectUuids,
@Nullable Collection<String> moduleUuids, Collection<String> directories, Collection<String> fileUuids) {

builder.authors(authors);

if (projectUuids != null) {
if (projects != null) {
throw new IllegalArgumentException("projects and projectUuids cannot be set simultaneously");

+ 2
- 1
server/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterParameters.java View File

@@ -54,6 +54,7 @@ public class IssueFilterParameters {
public static final String ACTION_PLANS = "actionPlans";
public static final String REPORTERS = "reporters";
public static final String ASSIGNEES = "assignees";
public static final String AUTHORS = "authors";
public static final String LANGUAGES = "languages";
public static final String TAGS = "tags";
public static final String ASSIGNED = "assigned";
@@ -70,7 +71,7 @@ public class IssueFilterParameters {

public static final List<String> ALL = ImmutableList.of(ISSUES, SEVERITIES, STATUSES, RESOLUTIONS, RESOLVED, COMPONENTS, COMPONENT_ROOTS, RULES, ACTION_PLANS, REPORTERS, TAGS,
ASSIGNEES, LANGUAGES, ASSIGNED, PLANNED, HIDE_RULES, CREATED_AT, CREATED_AFTER, CREATED_BEFORE, PAGE_SIZE, PAGE_INDEX, SORT, ASC, COMPONENT_UUIDS, COMPONENT_ROOT_UUIDS,
PROJECTS, PROJECT_UUIDS, IGNORE_PAGING, PROJECT_KEYS, COMPONENT_KEYS, MODULE_UUIDS, DIRECTORIES, FILE_UUIDS);
PROJECTS, PROJECT_UUIDS, IGNORE_PAGING, PROJECT_KEYS, COMPONENT_KEYS, MODULE_UUIDS, DIRECTORIES, FILE_UUIDS, AUTHORS);

public static final List<String> ALL_WITHOUT_PAGINATION = newArrayList(Iterables.filter(ALL, new Predicate<String>() {
@Override

+ 6
- 3
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java View File

@@ -249,6 +249,7 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
filters.put(IssueIndexDefinition.FIELD_ISSUE_TAGS, matchFilter(IssueIndexDefinition.FIELD_ISSUE_TAGS, query.tags()));
filters.put(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, matchFilter(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, query.resolutions()));
filters.put(IssueIndexDefinition.FIELD_ISSUE_REPORTER, matchFilter(IssueIndexDefinition.FIELD_ISSUE_REPORTER, query.reporters()));
filters.put(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, matchFilter(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, query.authors()));
filters.put(IssueIndexDefinition.FIELD_ISSUE_RULE_KEY, matchFilter(IssueIndexDefinition.FIELD_ISSUE_RULE_KEY, query.rules()));
filters.put(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, matchFilter(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, query.severities()));
filters.put(IssueIndexDefinition.FIELD_ISSUE_STATUS, matchFilter(IssueIndexDefinition.FIELD_ISSUE_STATUS, query.statuses()));
@@ -260,13 +261,13 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {

private void addComponentRelatedFilters(IssueQuery query, Map<String, FilterBuilder> filters) {
FilterBuilder viewFilter = viewFilter(query.viewUuids());
FilterBuilder componentFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.componentUuids());
FilterBuilder projectFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, query.projectUuids());
FilterBuilder moduleRootFilter = moduleRootFilter(query.moduleRootUuids());
FilterBuilder moduleFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, query.moduleUuids());
FilterBuilder directoryRootFilter = directoryFilter(query.moduleUuids(), query.directories());
FilterBuilder directoryRootFilter = directoryRootFilter(query.moduleUuids(), query.directories());
FilterBuilder directoryFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, query.directories());
FilterBuilder fileFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.fileUuids());
FilterBuilder componentFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, query.componentUuids());

if (BooleanUtils.isTrue(query.isContextualized())) {
if (viewFilter != null) {
@@ -328,7 +329,7 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
}

@CheckForNull
private FilterBuilder directoryFilter(Collection<String> moduleUuids, Collection<String> directoryPaths) {
private FilterBuilder directoryRootFilter(Collection<String> moduleUuids, Collection<String> directoryPaths) {
BoolFilterBuilder directoryTop = null;
FilterBuilder moduleFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, moduleUuids);
FilterBuilder directoryFilter = matchFilter(IssueIndexDefinition.FIELD_ISSUE_DIRECTORY_PATH, directoryPaths);
@@ -422,6 +423,8 @@ public class IssueIndex extends BaseIndex<Issue, FakeIssueDto, String> {
IssueFilterParameters.RULES, IssueIndexDefinition.FIELD_ISSUE_RULE_KEY, query.rules().toArray());
addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch,
IssueFilterParameters.REPORTERS, IssueIndexDefinition.FIELD_ISSUE_REPORTER);
addSimpleStickyFacetIfNeeded(options, stickyFacetBuilder, esSearch,
IssueFilterParameters.AUTHORS, IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, query.authors().toArray());

if (options.facets().contains(IssueFilterParameters.TAGS)) {
esSearch.addAggregation(stickyFacetBuilder.buildStickyFacet(IssueIndexDefinition.FIELD_ISSUE_TAGS, IssueFilterParameters.TAGS, TAGS_FACET_SIZE, query.tags().toArray()));

+ 4
- 0
server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java View File

@@ -158,6 +158,9 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
action.createParam(IssueFilterParameters.REPORTERS)
.setDescription("Comma-separated list of reporter logins")
.setExampleValue("admin");
action.createParam(IssueFilterParameters.AUTHORS)
.setDescription("Comma-separated list of SCM accounts")
.setExampleValue("torvalds@linux-foundation.org");
action.createParam(IssueFilterParameters.ASSIGNEES)
.setDescription("Comma-separated list of assignee logins")
.setExampleValue("admin,usera");
@@ -288,6 +291,7 @@ public class SearchAction extends SearchRequestHandler<IssueQuery, Issue> {
IssueFilterParameters.RULES,
IssueFilterParameters.ASSIGNEES,
IssueFilterParameters.REPORTERS,
IssueFilterParameters.AUTHORS,
IssueFilterParameters.MODULE_UUIDS,
IssueFilterParameters.FILE_UUIDS,
IssueFilterParameters.DIRECTORIES,

+ 18
- 1
server/sonar-server/src/test/java/org/sonar/server/component/ComponentTesting.java View File

@@ -24,7 +24,6 @@ import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Scopes;
import org.sonar.api.utils.internal.Uuids;
import org.sonar.core.component.ComponentDto;

import static org.sonar.core.component.ComponentDto.MODULE_UUID_PATH_SEP;

public class ComponentTesting {
@@ -90,6 +89,24 @@ public class ComponentTesting {
.setEnabled(true);
}

public static ComponentDto newDeveloper(String name) {
String uuid = "DEV:" + name;
return new ComponentDto()
.setUuid(uuid)
.setProjectUuid(uuid)
.setModuleUuidPath(MODULE_UUID_PATH_SEP)
.setParentProjectId(null)
.setKey(uuid)
.setName(name)
.setLongName(name)
.setScope(Scopes.PROJECT)
// XXX No constant !
.setQualifier("DEV")
.setPath(null)
.setLanguage(null)
.setEnabled(true);
}

private static ComponentDto newComponent(String uuid, ComponentDto module) {
return new ComponentDto()
.setUuid(uuid)

+ 5
- 1
server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryServiceTest.java View File

@@ -33,6 +33,7 @@ import org.sonar.api.resources.Qualifiers;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.utils.DateUtils;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.user.AuthorDao;
import org.sonar.server.component.ComponentService;
import org.sonar.server.component.db.ComponentDao;
import org.sonar.server.db.DbClient;
@@ -63,6 +64,9 @@ public class IssueQueryServiceTest {
@Mock
ComponentDao componentDao;

@Mock
AuthorDao authorDao;

@Mock
ComponentService componentService;

@@ -81,7 +85,7 @@ public class IssueQueryServiceTest {
}
});

issueQueryService = new IssueQueryService(dbClient, componentService);
issueQueryService = new IssueQueryService(dbClient, componentService, authorDao);
}

@Test

+ 31
- 0
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java View File

@@ -572,6 +572,37 @@ public class IssueIndexMediumTest {
assertThat(index.search(IssueQuery.builder().reporters(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty();
}

@Test
public void filter_by_authors() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();
ComponentDto file = ComponentTesting.newFileDto(project);

indexIssues(
IssueTesting.newDoc("ISSUE1", file).setAuthorLogin("steph"),
IssueTesting.newDoc("ISSUE2", file).setAuthorLogin("simon"),
IssueTesting.newDoc("ISSUE3", file).setAssignee(null));

assertThat(index.search(IssueQuery.builder().authors(newArrayList("steph")).build(), new QueryContext()).getHits()).hasSize(1);
assertThat(index.search(IssueQuery.builder().authors(newArrayList("steph", "simon")).build(), new QueryContext()).getHits()).hasSize(2);
assertThat(index.search(IssueQuery.builder().authors(newArrayList("unknown")).build(), new QueryContext()).getHits()).isEmpty();
}

@Test
public void facets_on_authors() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();
ComponentDto file = ComponentTesting.newFileDto(project);

indexIssues(
IssueTesting.newDoc("ISSUE1", file).setAuthorLogin("steph"),
IssueTesting.newDoc("ISSUE2", file).setAuthorLogin("simon"),
IssueTesting.newDoc("ISSUE3", file).setAuthorLogin("simon"),
IssueTesting.newDoc("ISSUE4", file).setAuthorLogin(null));

Result<Issue> result = index.search(IssueQuery.builder().build(), new QueryContext().addFacets(newArrayList("authors")));
assertThat(result.getFacets()).containsOnlyKeys("authors");
assertThat(result.getFacets().get("authors")).containsOnly(new FacetValue("steph", 1), new FacetValue("simon", 2));
}

@Test
public void filter_by_created_after() throws Exception {
ComponentDto project = ComponentTesting.newProjectDto();

+ 48
- 0
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java View File

@@ -455,6 +455,54 @@ public class SearchActionComponentsMediumTest {
.assertJson(this.getClass(), "no_issue.json", false);
}

public void search_by_author() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
RuleDto newRule = newRule();
IssueDto issue1 = IssueTesting.newDto(newRule, file, project).setAuthorLogin("leia").setKee("2bd4eac2-b650-4037-80bc-7b112bd4eac2");
IssueDto issue2 = IssueTesting.newDto(newRule, file, project).setAuthorLogin("luke@skywalker.name").setKee("82fd47d4-b650-4037-80bc-7b1182fd47d4");

db.issueDao().insert(session, issue1, issue2);
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
.setParam(IssueFilterParameters.AUTHORS, "leia")
.setParam(SearchAction.PARAM_FACETS, "authors")
.execute()
.assertJson(this.getClass(), "search_by_authors.json", false);

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
.setParam(IssueFilterParameters.AUTHORS, "unknown")
.execute()
.assertJson(this.getClass(), "no_issue.json", false);

}

@Test
public void search_by_developer() throws Exception {
ComponentDto project = insertComponent(ComponentTesting.newProjectDto("ABCD").setKey("MyProject"));
setDefaultProjectPermission(project);
ComponentDto file = insertComponent(ComponentTesting.newFileDto(project, "BCDE").setKey("MyComponent"));
ComponentDto developer = insertComponent(ComponentTesting.newDeveloper("Anakin Skywalker"));
db.authorDao().insertAuthor("vader", developer.getId());
db.authorDao().insertAuthor("anakin@skywalker.name", developer.getId());
RuleDto newRule = newRule();
IssueDto issue1 = IssueTesting.newDto(newRule, file, project).setAuthorLogin("vader").setKee("2bd4eac2-b650-4037-80bc-7b112bd4eac2");
IssueDto issue2 = IssueTesting.newDto(newRule, file, project).setAuthorLogin("anakin@skywalker.name").setKee("82fd47d4-b650-4037-80bc-7b1182fd47d4");


db.issueDao().insert(session, issue1, issue2);
session.commit();
tester.get(IssueIndexer.class).indexAll();

wsTester.newGetRequest(IssuesWs.API_ENDPOINT, SearchAction.SEARCH_ACTION)
.setParam(IssueFilterParameters.COMPONENT_UUIDS, developer.uuid())
.execute()
.assertJson(this.getClass(), "search_by_developer.json", false);
}

private RuleDto newRule() {
RuleDto rule = RuleTesting.newXooX1()
.setName("Rule name")

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java View File

@@ -88,7 +88,7 @@ public class SearchActionMediumTest {
assertThat(show.isPost()).isFalse();
assertThat(show.isInternal()).isFalse();
assertThat(show.responseExampleAsString()).isNotEmpty();
assertThat(show.params()).hasSize(38);
assertThat(show.params()).hasSize(39);
}

@Test

+ 28
- 0
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_authors.json View File

@@ -0,0 +1,28 @@
{
"total": 1,
"p": 1,
"issues": [
{
"key": "2bd4eac2-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"rule": "xoo:x1",
"author": "leia"
}
],
"facets": [
{
"property": "authors",
"values": [
{
"val": "leia",
"count": 1
},
{
"val": "luke@skywalker.name",
"count": 1
}
]
}
]
}

+ 21
- 0
server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_developer.json View File

@@ -0,0 +1,21 @@
{
"total": 2,
"p": 1,
"issues": [
{
"key": "2bd4eac2-b650-4037-80bc-7b112bd4eac2",
"component": "MyComponent",
"project": "MyProject",
"rule": "xoo:x1",
"author": "vader"
},
{
"key": "82fd47d4-b650-4037-80bc-7b1182fd47d4",
"component": "MyComponent",
"project": "MyProject",
"rule": "xoo:x1",
"author": "anakin@skywalker.name"
}
]
}

+ 25
- 1
sonar-core/src/main/java/org/sonar/core/user/AuthorDao.java View File

@@ -19,23 +19,29 @@
*/
package org.sonar.core.user;

import org.sonar.core.persistence.DaoComponent;

import com.google.common.base.Function;
import com.google.common.base.Strings;
import org.apache.ibatis.session.SqlSession;
import org.sonar.api.BatchComponent;
import org.sonar.api.ServerComponent;
import org.sonar.core.component.ComponentDto;
import org.sonar.core.persistence.DaoUtils;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.resource.ResourceDao;
import org.sonar.core.resource.ResourceDto;

import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
* @since 3.0
*
* Be careful when updating this class because it's used by the Dev Cockpit plugin.
*/
public class AuthorDao implements BatchComponent, ServerComponent {
public class AuthorDao implements BatchComponent, ServerComponent, DaoComponent {

private final MyBatis mybatis;
private final ResourceDao resourceDao;
@@ -101,4 +107,22 @@ public class AuthorDao implements BatchComponent, ServerComponent {

authorMapper.insert(authorDto);
}

public List<String> selectScmAccountsByDeveloperUuids(Collection<String> developerUuid) {
SqlSession session = mybatis.openSession(false);
try {
return selectScmAccountsByDeveloperUuids(session, developerUuid);
} finally {
MyBatis.closeQuietly(session);
}
}

public List<String> selectScmAccountsByDeveloperUuids(final SqlSession session, Collection<String> developerUuids) {
return DaoUtils.executeLargeInputs(developerUuids, new Function<List<String>, List<String>>() {
@Override
public List<String> apply(List<String> partition) {
return session.getMapper(AuthorMapper.class).selectScmAccountsByDeveloperUuids(partition);
}
});
}
}

+ 7
- 0
sonar-core/src/main/java/org/sonar/core/user/AuthorMapper.java View File

@@ -19,6 +19,11 @@
*/
package org.sonar.core.user;

import org.apache.ibatis.annotations.Param;

import java.util.Collection;
import java.util.List;

/**
* @since 3.0
*/
@@ -29,4 +34,6 @@ public interface AuthorMapper {
void insert(AuthorDto authorDto);

int countDeveloperLogins(long developerId);

List<String> selectScmAccountsByDeveloperUuids(@Param("uuids") Collection<String> uuids);
}

+ 11
- 0
sonar-core/src/main/resources/org/sonar/core/user/AuthorMapper.xml View File

@@ -19,4 +19,15 @@
#{createdAt}, #{updatedAt})
</insert>

<select id="selectScmAccountsByDeveloperUuids" parameterType="String" resultType="String">
SELECT a.login
FROM authors a
INNER JOIN projects p ON p.id=a.person_id
<where>
and p.uuid in
<foreach collection="uuids" open="(" close=")" item="uuid" separator=",">
#{uuid}
</foreach>
</where>
</select>
</mapper>

Loading…
Cancel
Save