@@ -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); | |||
} |
@@ -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; |
@@ -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"); |
@@ -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 |
@@ -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())); |
@@ -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, |
@@ -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) |
@@ -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 |
@@ -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(); |
@@ -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") |
@@ -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 |
@@ -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 | |||
} | |||
] | |||
} | |||
] | |||
} |
@@ -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" | |||
} | |||
] | |||
} |
@@ -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); | |||
} | |||
}); | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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> |