From 7a15c4d1064bbda06a204427bc71a26d9091647a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Lievremont Date: Thu, 29 Jan 2015 17:48:00 +0100 Subject: [PATCH] SONAR-6034 Add filter on authors and developers --- .../java/org/sonar/server/db/DbClient.java | 8 ++++ .../org/sonar/server/issue/IssueQuery.java | 12 +++++ .../sonar/server/issue/IssueQueryService.java | 30 ++++++++---- .../issue/filter/IssueFilterParameters.java | 3 +- .../sonar/server/issue/index/IssueIndex.java | 9 ++-- .../sonar/server/issue/ws/SearchAction.java | 4 ++ .../server/component/ComponentTesting.java | 19 +++++++- .../server/issue/IssueQueryServiceTest.java | 6 ++- .../issue/index/IssueIndexMediumTest.java | 31 ++++++++++++ .../ws/SearchActionComponentsMediumTest.java | 48 +++++++++++++++++++ .../issue/ws/SearchActionMediumTest.java | 2 +- .../search_by_authors.json | 28 +++++++++++ .../search_by_developer.json | 21 ++++++++ .../java/org/sonar/core/user/AuthorDao.java | 26 +++++++++- .../org/sonar/core/user/AuthorMapper.java | 7 +++ .../org/sonar/core/user/AuthorMapper.xml | 11 +++++ 16 files changed, 247 insertions(+), 18 deletions(-) create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_authors.json create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_developer.json diff --git a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java index 5b580190d3f..22cbf6fe36e 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java +++ b/server/sonar-server/src/main/java/org/sonar/server/db/DbClient.java @@ -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 getDao(Map map, Class clazz) { return (K) map.get(clazz); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java index 5116a2054a5..8067ca37021 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQuery.java @@ -68,6 +68,7 @@ public class IssueQuery { private final Collection actionPlans; private final Collection reporters; private final Collection assignees; + private final Collection authors; private final Collection languages; private final Collection 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 authors() { + return authors; + } + public Collection languages() { return languages; } @@ -271,6 +277,7 @@ public class IssueQuery { private Collection actionPlans; private Collection reporters; private Collection assignees; + private Collection authors; private Collection languages; private Collection tags; private Boolean onComponentOnly = false; @@ -364,6 +371,11 @@ public class IssueQuery { return this; } + public Builder authors(@Nullable Collection l) { + this.authors = l; + return this; + } + public Builder languages(@Nullable Collection l) { this.languages = l; return this; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java index da25046b31a..03e15349b03 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryService.java @@ -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 = ""; 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 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 projectUuids, @Nullable Collection projects, @Nullable Collection moduleUuids, @Nullable Collection directories, - @Nullable Collection fileUuids) { + @Nullable Collection fileUuids, + @Nullable Collection authors) { Set 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 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 authors, @Nullable Collection projects, @Nullable Collection projectUuids, @Nullable Collection moduleUuids, Collection directories, Collection fileUuids) { + + builder.authors(authors); + if (projectUuids != null) { if (projects != null) { throw new IllegalArgumentException("projects and projectUuids cannot be set simultaneously"); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterParameters.java b/server/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterParameters.java index bef2e2c85c8..bb2533c9b39 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterParameters.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/filter/IssueFilterParameters.java @@ -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 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 ALL_WITHOUT_PAGINATION = newArrayList(Iterables.filter(ALL, new Predicate() { @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java index 2b9a62171af..89c069e42d7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -249,6 +249,7 @@ public class IssueIndex extends BaseIndex { 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 { private void addComponentRelatedFilters(IssueQuery query, Map 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 { } @CheckForNull - private FilterBuilder directoryFilter(Collection moduleUuids, Collection directoryPaths) { + private FilterBuilder directoryRootFilter(Collection moduleUuids, Collection 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 { 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())); diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java index dbf72d30a71..287313e846f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java @@ -158,6 +158,9 @@ public class SearchAction extends SearchRequestHandler { 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 { IssueFilterParameters.RULES, IssueFilterParameters.ASSIGNEES, IssueFilterParameters.REPORTERS, + IssueFilterParameters.AUTHORS, IssueFilterParameters.MODULE_UUIDS, IssueFilterParameters.FILE_UUIDS, IssueFilterParameters.DIRECTORIES, diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentTesting.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentTesting.java index 5d6091370cd..178ccb0c78e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentTesting.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentTesting.java @@ -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) diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryServiceTest.java index 379e15a484f..dfef64e3501 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueQueryServiceTest.java @@ -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 diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java index 97cd5b02704..51f58191fb0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java @@ -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 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(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java index 55dcc9b90ce..5a50b56856c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsMediumTest.java @@ -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") diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java index c6db9ae80a3..8111464b588 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionMediumTest.java @@ -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 diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_authors.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_authors.json new file mode 100644 index 00000000000..3be7b368db8 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_authors.json @@ -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 + } + ] + } + ] +} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_developer.json b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_developer.json new file mode 100644 index 00000000000..0f0bc742c5e --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/ws/SearchActionComponentsMediumTest/search_by_developer.json @@ -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" + } + + ] +} diff --git a/sonar-core/src/main/java/org/sonar/core/user/AuthorDao.java b/sonar-core/src/main/java/org/sonar/core/user/AuthorDao.java index 224d7201cb3..a5a9756c8ee 100644 --- a/sonar-core/src/main/java/org/sonar/core/user/AuthorDao.java +++ b/sonar-core/src/main/java/org/sonar/core/user/AuthorDao.java @@ -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 selectScmAccountsByDeveloperUuids(Collection developerUuid) { + SqlSession session = mybatis.openSession(false); + try { + return selectScmAccountsByDeveloperUuids(session, developerUuid); + } finally { + MyBatis.closeQuietly(session); + } + } + + public List selectScmAccountsByDeveloperUuids(final SqlSession session, Collection developerUuids) { + return DaoUtils.executeLargeInputs(developerUuids, new Function, List>() { + @Override + public List apply(List partition) { + return session.getMapper(AuthorMapper.class).selectScmAccountsByDeveloperUuids(partition); + } + }); + } } diff --git a/sonar-core/src/main/java/org/sonar/core/user/AuthorMapper.java b/sonar-core/src/main/java/org/sonar/core/user/AuthorMapper.java index 65f485c6278..0d4c09bf3d8 100644 --- a/sonar-core/src/main/java/org/sonar/core/user/AuthorMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/user/AuthorMapper.java @@ -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 selectScmAccountsByDeveloperUuids(@Param("uuids") Collection uuids); } diff --git a/sonar-core/src/main/resources/org/sonar/core/user/AuthorMapper.xml b/sonar-core/src/main/resources/org/sonar/core/user/AuthorMapper.xml index f4d03658b08..f12ea11d5d4 100644 --- a/sonar-core/src/main/resources/org/sonar/core/user/AuthorMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/user/AuthorMapper.xml @@ -19,4 +19,15 @@ #{createdAt}, #{updatedAt}) + -- 2.39.5