@@ -34,6 +34,7 @@ public class ComponentQuery { | |||
private final String language; | |||
private final Boolean isPrivate; | |||
private final Set<Long> componentIds; | |||
private final Long analyzedBefore; | |||
/** | |||
* Used by Dev Cockpit 1.9. | |||
@@ -48,6 +49,7 @@ public class ComponentQuery { | |||
this.language = null; | |||
this.componentIds = null; | |||
this.isPrivate = null; | |||
this.analyzedBefore = null; | |||
} | |||
private ComponentQuery(Builder builder) { | |||
@@ -56,6 +58,7 @@ public class ComponentQuery { | |||
this.language = builder.language; | |||
this.componentIds = builder.componentIds; | |||
this.isPrivate = builder.isPrivate; | |||
this.analyzedBefore = builder.analyzedBefore; | |||
} | |||
public String[] getQualifiers() { | |||
@@ -90,6 +93,11 @@ public class ComponentQuery { | |||
return isPrivate; | |||
} | |||
@CheckForNull | |||
public Long getAnalyzedBefore() { | |||
return analyzedBefore; | |||
} | |||
public static Builder builder() { | |||
return new Builder(); | |||
} | |||
@@ -100,6 +108,7 @@ public class ComponentQuery { | |||
private String language; | |||
private Boolean isPrivate; | |||
private Set<Long> componentIds; | |||
private Long analyzedBefore; | |||
public Builder setNameOrKeyQuery(@Nullable String nameOrKeyQuery) { | |||
this.nameOrKeyQuery = nameOrKeyQuery; | |||
@@ -126,6 +135,11 @@ public class ComponentQuery { | |||
return this; | |||
} | |||
public Builder setAnalyzedBefore(@Nullable Long analyzedBefore) { | |||
this.analyzedBefore = analyzedBefore; | |||
return this; | |||
} | |||
protected static String[] validateQualifiers(@Nullable String... qualifiers) { | |||
checkArgument(qualifiers != null && qualifiers.length > 0, "At least one qualifier must be provided"); | |||
return qualifiers; |
@@ -249,23 +249,27 @@ | |||
<sql id="sqlSelectByQuery"> | |||
from projects p | |||
<if test="query.analyzedBefore!=null"> | |||
inner join snapshots s on s.component_uuid=p.uuid and s.status='P' and s.islast=${_true} | |||
and s.created_at < #{query.analyzedBefore,jdbcType=BIGINT} | |||
</if> | |||
where | |||
p.enabled=${_true} | |||
AND p.copy_component_uuid is null | |||
and p.copy_component_uuid is null | |||
<if test="organizationUuid!=null"> | |||
and p.organization_uuid=#{organizationUuid,jdbcType=VARCHAR} | |||
</if> | |||
<if test="query.qualifiers!=null"> | |||
AND p.qualifier in | |||
<foreach collection="query.qualifiers" item="qualifier" open="(" close=")" separator=","> | |||
#{qualifier,jdbcType=VARCHAR} | |||
</foreach> | |||
and p.qualifier in | |||
<foreach collection="query.qualifiers" item="qualifier" open="(" close=")" separator=","> | |||
#{qualifier,jdbcType=VARCHAR} | |||
</foreach> | |||
</if> | |||
<if test="query.language!=null"> | |||
AND p.language = #{query.language,jdbcType=VARCHAR} | |||
and p.language = #{query.language,jdbcType=VARCHAR} | |||
</if> | |||
<if test="query.componentIds!=null"> | |||
AND p.id in | |||
and p.id in | |||
<foreach collection="query.componentIds" item="componentId" open="(" close=")" separator=","> | |||
#{componentId,jdbcType=BIGINT} | |||
</foreach> |
@@ -58,6 +58,7 @@ import static org.sonar.db.component.ComponentTesting.newSubView; | |||
import static org.sonar.db.component.ComponentTesting.newView; | |||
import static org.sonar.db.component.ComponentTreeQuery.Strategy.CHILDREN; | |||
import static org.sonar.db.component.ComponentTreeQuery.Strategy.LEAVES; | |||
import static org.sonar.db.component.SnapshotTesting.newAnalysis; | |||
public class ComponentDaoTest { | |||
@@ -984,6 +985,25 @@ public class ComponentDaoTest { | |||
assertThat(result.get(0).getDbKey()).isEqualTo("java-project-key"); | |||
} | |||
@Test | |||
public void selectByQuery_filter_on_last_analysis_date() { | |||
long aLongTimeAgo = 1_000_000_000L; | |||
long recentTime = 3_000_000_000L; | |||
ComponentDto oldProject = db.components().insertPublicProject(); | |||
db.getDbClient().snapshotDao().insert(dbSession, newAnalysis(oldProject).setCreatedAt(aLongTimeAgo)); | |||
ComponentDto recentProject = db.components().insertPublicProject(); | |||
db.getDbClient().snapshotDao().insert(dbSession, newAnalysis(recentProject).setCreatedAt(recentTime)); | |||
db.getDbClient().snapshotDao().insert(dbSession, newAnalysis(recentProject).setCreatedAt(aLongTimeAgo).setLast(false)); | |||
ComponentQuery.Builder query = ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT); | |||
assertThat(underTest.selectByQuery(dbSession, query.setAnalyzedBefore(recentTime).build(), 0, 10)).extracting(ComponentDto::getKey) | |||
.containsExactlyInAnyOrder(oldProject.getKey()); | |||
assertThat(underTest.selectByQuery(dbSession, query.setAnalyzedBefore(aLongTimeAgo).build(), 0, 10)).extracting(ComponentDto::getKey) | |||
.isEmpty(); | |||
assertThat(underTest.selectByQuery(dbSession, query.setAnalyzedBefore(recentTime + 1_000L).build(), 0, 10)).extracting(ComponentDto::getKey) | |||
.containsExactlyInAnyOrder(oldProject.getKey(), recentProject.getKey()); | |||
} | |||
@Test | |||
public void selectByQuery_filter_on_visibility() { | |||
db.components().insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setDbKey("private-key")); |
@@ -36,12 +36,14 @@ public class ComponentQueryTest { | |||
ComponentQuery underTest = ComponentQuery.builder() | |||
.setNameOrKeyQuery("key") | |||
.setLanguage("java") | |||
.setAnalyzedBefore(1_000_000_000L) | |||
.setQualifiers(PROJECT) | |||
.build(); | |||
assertThat(underTest.getNameOrKeyQuery()).isEqualTo("key"); | |||
assertThat(underTest.getLanguage()).isEqualTo("java"); | |||
assertThat(underTest.getQualifiers()).containsOnly(PROJECT); | |||
assertThat(underTest.getAnalyzedBefore()).isEqualTo(1_000_000_000L); | |||
} | |||
@Test |
@@ -20,16 +20,20 @@ | |||
package org.sonar.server.project.ws; | |||
import java.util.List; | |||
import java.util.Map; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.server.ws.Change; | |||
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.Param; | |||
import org.sonar.api.utils.Paging; | |||
import org.sonar.core.util.stream.MoreCollectors; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.component.ComponentQuery; | |||
import org.sonar.db.component.SnapshotDto; | |||
import org.sonar.db.organization.OrganizationDto; | |||
import org.sonar.db.permission.OrganizationPermission; | |||
import org.sonar.server.organization.DefaultOrganizationProvider; | |||
@@ -43,6 +47,8 @@ import static java.util.Optional.ofNullable; | |||
import static org.sonar.api.resources.Qualifiers.APP; | |||
import static org.sonar.api.resources.Qualifiers.PROJECT; | |||
import static org.sonar.api.resources.Qualifiers.VIEW; | |||
import static org.sonar.api.utils.DateUtils.formatDateTime; | |||
import static org.sonar.api.utils.DateUtils.parseDateOrDateTime; | |||
import static org.sonar.core.util.Protobuf.setNullable; | |||
import static org.sonar.server.project.Visibility.PRIVATE; | |||
import static org.sonar.server.project.Visibility.PUBLIC; | |||
@@ -51,6 +57,7 @@ import static org.sonarqube.ws.WsProjects.SearchWsResponse.Component; | |||
import static org.sonarqube.ws.WsProjects.SearchWsResponse.newBuilder; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_SEARCH; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.MAX_PAGE_SIZE; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ANALYZED_BEFORE; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ORGANIZATION; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_VISIBILITY; | |||
@@ -97,6 +104,11 @@ public class SearchAction implements ProjectsWsAction { | |||
.setInternal(true) | |||
.setSince("6.4") | |||
.setPossibleValues(Visibility.getLabels()); | |||
action.createParam(PARAM_ANALYZED_BEFORE) | |||
.setDescription("Filter the projects for which last analysis is older than the given date (exclusive).<br> " + | |||
"Format: date or datetime ISO formats.") | |||
.setSince("6.6"); | |||
} | |||
@Override | |||
@@ -113,6 +125,7 @@ public class SearchAction implements ProjectsWsAction { | |||
.setPage(request.mandatoryParamAsInt(Param.PAGE)) | |||
.setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)) | |||
.setVisibility(request.param(PARAM_VISIBILITY)) | |||
.setAnalyzedBefore(request.param(PARAM_ANALYZED_BEFORE)) | |||
.build(); | |||
} | |||
@@ -124,7 +137,10 @@ public class SearchAction implements ProjectsWsAction { | |||
ComponentQuery query = buildQuery(request); | |||
Paging paging = buildPaging(dbSession, request, organization, query); | |||
List<ComponentDto> components = dbClient.componentDao().selectByQuery(dbSession, organization.getUuid(), query, paging.offset(), paging.pageSize()); | |||
return buildResponse(components, organization, paging); | |||
Map<String, Long> analysisDateByComponentUuid = dbClient.snapshotDao() | |||
.selectLastAnalysesByRootComponentUuids(dbSession, components.stream().map(ComponentDto::uuid).collect(MoreCollectors.toList())).stream() | |||
.collect(MoreCollectors.uniqueIndex(SnapshotDto::getComponentUuid, SnapshotDto::getCreatedAt)); | |||
return buildResponse(components, organization, analysisDateByComponentUuid, paging); | |||
} | |||
} | |||
@@ -135,6 +151,7 @@ public class SearchAction implements ProjectsWsAction { | |||
.setQualifiers(qualifiers.toArray(new String[qualifiers.size()])); | |||
setNullable(request.getVisibility(), v -> query.setPrivate(Visibility.isPrivate(v))); | |||
setNullable(request.getAnalyzedBefore(), d -> query.setAnalyzedBefore(parseDateOrDateTime(d).getTime())); | |||
return query.build(); | |||
} | |||
@@ -146,7 +163,7 @@ public class SearchAction implements ProjectsWsAction { | |||
.andTotal(total); | |||
} | |||
private static SearchWsResponse buildResponse(List<ComponentDto> components, OrganizationDto organization, Paging paging) { | |||
private static SearchWsResponse buildResponse(List<ComponentDto> components, OrganizationDto organization, Map<String, Long> analysisDateByComponentUuid, Paging paging) { | |||
SearchWsResponse.Builder responseBuilder = newBuilder(); | |||
responseBuilder.getPagingBuilder() | |||
.setPageIndex(paging.pageIndex()) | |||
@@ -155,12 +172,12 @@ public class SearchAction implements ProjectsWsAction { | |||
.build(); | |||
components.stream() | |||
.map(dto -> dtoToProject(organization, dto)) | |||
.map(dto -> dtoToProject(organization, dto, analysisDateByComponentUuid.get(dto.uuid()))) | |||
.forEach(responseBuilder::addComponents); | |||
return responseBuilder.build(); | |||
} | |||
private static Component dtoToProject(OrganizationDto organization, ComponentDto dto) { | |||
private static Component dtoToProject(OrganizationDto organization, ComponentDto dto, @Nullable Long analysisDate) { | |||
checkArgument( | |||
organization.getUuid().equals(dto.getOrganizationUuid()), | |||
"No Organization found for uuid '%s'", | |||
@@ -173,6 +190,8 @@ public class SearchAction implements ProjectsWsAction { | |||
.setName(dto.name()) | |||
.setQualifier(dto.qualifier()) | |||
.setVisibility(dto.isPrivate() ? PRIVATE.getLabel() : PUBLIC.getLabel()); | |||
setNullable(analysisDate, d -> builder.setLastAnalysisDate(formatDateTime(d))); | |||
return builder.build(); | |||
} | |||
@@ -11,7 +11,8 @@ | |||
"key": "project-key-1", | |||
"name": "Project Name 1", | |||
"qualifier": "TRK", | |||
"visibility": "public" | |||
"visibility": "public", | |||
"lastAnalysisDate": "2017-03-01T11:39:03+0300" | |||
}, | |||
{ | |||
"organization": "my-org-1", | |||
@@ -19,7 +20,8 @@ | |||
"key": "project-key-2", | |||
"name": "Project Name 1", | |||
"qualifier": "TRK", | |||
"visibility": "private" | |||
"visibility": "private", | |||
"lastAnalysisDate": "2017-03-02T15:21:47+0300" | |||
} | |||
] | |||
} |
@@ -23,6 +23,7 @@ import com.google.common.base.Joiner; | |||
import java.io.IOException; | |||
import java.net.URISyntaxException; | |||
import java.util.ArrayList; | |||
import java.util.Date; | |||
import java.util.List; | |||
import org.assertj.core.api.Assertions; | |||
import org.junit.Rule; | |||
@@ -54,15 +55,19 @@ import static org.mockito.Mockito.mock; | |||
import static org.sonar.api.server.ws.WebService.Param.PAGE; | |||
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE; | |||
import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY; | |||
import static org.sonar.api.utils.DateUtils.formatDate; | |||
import static org.sonar.api.utils.DateUtils.parseDateTime; | |||
import static org.sonar.core.util.Protobuf.setNullable; | |||
import static org.sonar.db.component.ComponentTesting.newDirectory; | |||
import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
import static org.sonar.db.component.ComponentTesting.newModuleDto; | |||
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; | |||
import static org.sonar.db.component.ComponentTesting.newView; | |||
import static org.sonar.db.component.SnapshotTesting.newAnalysis; | |||
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; | |||
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_PROFILES; | |||
import static org.sonar.test.JsonAssert.assertJson; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ANALYZED_BEFORE; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ORGANIZATION; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_VISIBILITY; | |||
@@ -204,6 +209,24 @@ public class SearchActionTest { | |||
assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly(project1.getDbKey(), project2.getDbKey()); | |||
} | |||
@Test | |||
public void search_for_old_projects() { | |||
userSession.addPermission(ADMINISTER, db.getDefaultOrganization()); | |||
long aLongTimeAgo = 1_000_000_000L; | |||
long recentTime = 3_000_000_000L; | |||
ComponentDto oldProject = db.components().insertPublicProject(); | |||
db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(oldProject).setCreatedAt(aLongTimeAgo)); | |||
ComponentDto recentProject = db.components().insertPublicProject(); | |||
db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(recentProject).setCreatedAt(recentTime)); | |||
db.commit(); | |||
SearchWsResponse result = call(SearchWsRequest.builder().setAnalyzedBefore(formatDate(new Date(recentTime))).build()); | |||
assertThat(result.getComponentsList()).extracting(Component::getKey) | |||
.containsExactlyInAnyOrder(oldProject.getKey()) | |||
.doesNotContain(recentProject.getKey()); | |||
} | |||
@Test | |||
public void result_is_paginated() throws IOException { | |||
userSession.addPermission(ADMINISTER, db.getDefaultOrganization()); | |||
@@ -243,7 +266,7 @@ public class SearchActionTest { | |||
} | |||
@Test | |||
public void verify_define() { | |||
public void definition() { | |||
WebService.Action action = ws.getDef(); | |||
assertThat(action.key()).isEqualTo("search"); | |||
assertThat(action.isPost()).isFalse(); | |||
@@ -251,7 +274,7 @@ public class SearchActionTest { | |||
assertThat(action.isInternal()).isTrue(); | |||
assertThat(action.since()).isEqualTo("6.3"); | |||
assertThat(action.handler()).isEqualTo(ws.getDef().handler()); | |||
assertThat(action.params()).hasSize(6); | |||
assertThat(action.params()).hasSize(7); | |||
assertThat(action.responseExample()).isEqualTo(getClass().getResource("search-example.json")); | |||
WebService.Param organization = action.param("organization"); | |||
@@ -284,21 +307,32 @@ public class SearchActionTest { | |||
assertThat(visibilityParam.isRequired()).isFalse(); | |||
assertThat(visibilityParam.description()).isEqualTo("Filter the projects that should be visible to everyone (public), or only specific user/groups (private).<br/>" + | |||
"If no visibility is specified, the default project visibility of the organization will be used."); | |||
WebService.Param lastAnalysisBefore = action.param("analyzedBefore"); | |||
assertThat(lastAnalysisBefore.isRequired()).isFalse(); | |||
assertThat(lastAnalysisBefore.since()).isEqualTo("6.6"); | |||
} | |||
@Test | |||
public void verify_response_example() throws URISyntaxException, IOException { | |||
public void json_example() throws URISyntaxException, IOException { | |||
OrganizationDto organization = db.organizations().insertForKey("my-org-1"); | |||
userSession.addPermission(ADMINISTER, organization); | |||
ComponentDto publicProject = newPrivateProjectDto(organization, "project-uuid-1").setName("Project Name 1").setDbKey("project-key-1").setPrivate(false); | |||
ComponentDto privateProject = newPrivateProjectDto(organization, "project-uuid-2").setName("Project Name 1").setDbKey("project-key-2"); | |||
db.components().insertComponents( | |||
newPrivateProjectDto(organization, "project-uuid-1").setName("Project Name 1").setDbKey("project-key-1").setPrivate(false), | |||
newPrivateProjectDto(organization, "project-uuid-2").setName("Project Name 1").setDbKey("project-key-2")); | |||
publicProject, | |||
privateProject); | |||
db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(publicProject).setCreatedAt(parseDateTime("2017-03-01T11:39:03+0300").getTime())); | |||
db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(privateProject).setCreatedAt(parseDateTime("2017-03-02T15:21:47+0300").getTime())); | |||
db.commit(); | |||
String response = ws.newRequest() | |||
.setMediaType(MediaTypes.JSON) | |||
.setParam(PARAM_ORGANIZATION, organization.getKey()) | |||
.execute().getInput(); | |||
assertJson(response).isSimilarTo(ws.getDef().responseExampleAsString()); | |||
assertJson(ws.getDef().responseExampleAsString()).isSimilarTo(response); | |||
} | |||
private SearchWsResponse call(SearchWsRequest wsRequest) { | |||
@@ -312,6 +346,7 @@ public class SearchActionTest { | |||
setNullable(wsRequest.getPage(), page -> request.setParam(PAGE, String.valueOf(page))); | |||
setNullable(wsRequest.getPageSize(), pageSize -> request.setParam(PAGE_SIZE, String.valueOf(pageSize))); | |||
setNullable(wsRequest.getVisibility(), v -> request.setParam(PARAM_VISIBILITY, v)); | |||
setNullable(wsRequest.getAnalyzedBefore(), d -> request.setParam(PARAM_ANALYZED_BEFORE, d)); | |||
return request.executeProtobuf(SearchWsResponse.class); | |||
} | |||
@@ -31,12 +31,14 @@ import org.sonarqube.ws.client.WsConnector; | |||
import static org.sonar.api.server.ws.WebService.Param.PAGE; | |||
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE; | |||
import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY; | |||
import static org.sonar.api.utils.DateUtils.formatDateTimeNullSafe; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_BULK_UPDATE_KEY; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_CREATE; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_SEARCH; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_UPDATE_KEY; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_UPDATE_VISIBILITY; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.CONTROLLER; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ANALYZED_BEFORE; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_BRANCH; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_FROM; | |||
import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME; | |||
@@ -112,6 +114,7 @@ public class ProjectsService extends BaseService { | |||
GetRequest get = new GetRequest(path(ACTION_SEARCH)) | |||
.setParam(PARAM_ORGANIZATION, request.getOrganization()) | |||
.setParam(PARAM_QUALIFIERS, Joiner.on(",").join(request.getQualifiers())) | |||
.setParam(PARAM_ANALYZED_BEFORE, request.getAnalyzedBefore()) | |||
.setParam(TEXT_QUERY, request.getQuery()) | |||
.setParam(PAGE, request.getPage()) | |||
.setParam(PAGE_SIZE, request.getPageSize()); |
@@ -41,8 +41,9 @@ public class ProjectsWsParameters { | |||
public static final String PARAM_FROM = "from"; | |||
public static final String PARAM_TO = "to"; | |||
public static final String PARAM_DRY_RUN = "dryRun"; | |||
public static final String PARAM_VISIBILITY = "visibility"; | |||
public static final String PARAM_ANALYZED_BEFORE = "analyzedBefore"; | |||
public static final String FILTER_LANGUAGES = "languages"; | |||
public static final String FILTER_TAGS = "tags"; | |||
@@ -36,6 +36,7 @@ public class SearchWsRequest { | |||
private final String visibility; | |||
private final Integer page; | |||
private final Integer pageSize; | |||
private final String analyzedBefore; | |||
public SearchWsRequest(Builder builder) { | |||
this.organization = builder.organization; | |||
@@ -44,6 +45,7 @@ public class SearchWsRequest { | |||
this.visibility = builder.visibility; | |||
this.page = builder.page; | |||
this.pageSize = builder.pageSize; | |||
this.analyzedBefore = builder.analyzedBefore; | |||
} | |||
@CheckForNull | |||
@@ -75,6 +77,11 @@ public class SearchWsRequest { | |||
return visibility; | |||
} | |||
@CheckForNull | |||
public String getAnalyzedBefore() { | |||
return analyzedBefore; | |||
} | |||
public static Builder builder() { | |||
return new Builder(); | |||
} | |||
@@ -86,6 +93,7 @@ public class SearchWsRequest { | |||
private Integer pageSize; | |||
private String query; | |||
private String visibility; | |||
private String analyzedBefore; | |||
public Builder setOrganization(@Nullable String organization) { | |||
this.organization = organization; | |||
@@ -117,10 +125,14 @@ public class SearchWsRequest { | |||
return this; | |||
} | |||
public Builder setAnalyzedBefore(@Nullable String lastAnalysisBefore) { | |||
this.analyzedBefore = lastAnalysisBefore; | |||
return this; | |||
} | |||
public SearchWsRequest build() { | |||
checkArgument(pageSize == null || pageSize <= MAX_PAGE_SIZE, "Page size must not be greater than %s", MAX_PAGE_SIZE); | |||
return new SearchWsRequest(this); | |||
} | |||
} | |||
} |
@@ -70,6 +70,7 @@ message SearchWsResponse { | |||
optional string name = 4; | |||
optional string qualifier = 5; | |||
optional string visibility = 6; | |||
optional string lastAnalysisDate = 7; | |||
} | |||
} | |||
@@ -138,6 +138,7 @@ public class ProjectsServiceTest { | |||
.setOrganization("default") | |||
.setQuery("project") | |||
.setQualifiers(asList("TRK", "VW")) | |||
.setAnalyzedBefore("2017-09-01") | |||
.setPage(3) | |||
.setPageSize(10) | |||
.build()); | |||
@@ -146,6 +147,7 @@ public class ProjectsServiceTest { | |||
.hasPath("search") | |||
.hasParam("organization", "default") | |||
.hasParam("q", "project") | |||
.hasParam("analyzedBefore", "2017-09-01") | |||
.hasParam("qualifiers", "TRK,VW") | |||
.hasParam(PAGE, 3) | |||
.hasParam(PAGE_SIZE, 10) |
@@ -38,6 +38,7 @@ import org.sonarqube.tests.organization.RootUserOnOrganizationTest; | |||
import org.sonarqube.tests.projectAdministration.ProjectDeletionTest; | |||
import org.sonarqube.tests.projectAdministration.ProjectKeyUpdateTest; | |||
import org.sonarqube.tests.projectAdministration.ProjectProvisioningTest; | |||
import org.sonarqube.tests.projectAdministration.ProjectSearchTest; | |||
import org.sonarqube.tests.projectSearch.LeakProjectsPageTest; | |||
import org.sonarqube.tests.projectSearch.SearchProjectsTest; | |||
import org.sonarqube.tests.qualityGate.OrganizationQualityGateUiTest; | |||
@@ -79,6 +80,7 @@ import static util.ItUtils.xooPlugin; | |||
ProjectDeletionTest.class, | |||
ProjectProvisioningTest.class, | |||
ProjectKeyUpdateTest.class, | |||
ProjectSearchTest.class, | |||
PermissionTemplateTest.class | |||
}) | |||
public class Category6Suite { |
@@ -0,0 +1,76 @@ | |||
/* | |||
* 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.sonarqube.tests.projectAdministration; | |||
import com.sonar.orchestrator.Orchestrator; | |||
import java.util.Date; | |||
import org.apache.commons.lang.time.DateUtils; | |||
import org.junit.ClassRule; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.sonarqube.tests.Category6Suite; | |||
import org.sonarqube.tests.Tester; | |||
import org.sonarqube.ws.Organizations; | |||
import org.sonarqube.ws.WsProjects.CreateWsResponse; | |||
import org.sonarqube.ws.WsProjects.SearchWsResponse; | |||
import org.sonarqube.ws.client.project.SearchWsRequest; | |||
import static java.util.Collections.singletonList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static util.ItUtils.formatDate; | |||
import static util.ItUtils.runProjectAnalysis; | |||
public class ProjectSearchTest { | |||
@ClassRule | |||
public static Orchestrator orchestrator = Category6Suite.ORCHESTRATOR; | |||
@Rule | |||
public Tester tester = new Tester(orchestrator); | |||
@Test | |||
public void search_old_projects() { | |||
Organizations.Organization organization = tester.organizations().generate(); | |||
CreateWsResponse.Project oldProject = tester.projects().generate(organization); | |||
CreateWsResponse.Project recentProject = tester.projects().generate(organization); | |||
Date now = new Date(); | |||
Date oneYearAgo = DateUtils.addDays(now, -365); | |||
Date moreThanOneYearAgo = DateUtils.addDays(now, -366); | |||
analyzeProject(oldProject.getKey(), moreThanOneYearAgo, organization.getKey()); | |||
analyzeProject(recentProject.getKey(), now, organization.getKey()); | |||
SearchWsResponse result = tester.wsClient().projects().search(SearchWsRequest.builder() | |||
.setOrganization(organization.getKey()) | |||
.setQualifiers(singletonList("TRK")) | |||
.setAnalyzedBefore(formatDate(oneYearAgo)).build()); | |||
assertThat(result.getComponentsList()).extracting(SearchWsResponse.Component::getKey).containsExactlyInAnyOrder(oldProject.getKey()); | |||
} | |||
private void analyzeProject(String projectKey, Date analysisDate, String organizationKey) { | |||
runProjectAnalysis(orchestrator, "shared/xoo-sample", | |||
"sonar.organization", organizationKey, | |||
"sonar.projectKey", projectKey, | |||
"sonar.projectDate", formatDate(analysisDate), | |||
"sonar.login", "admin", | |||
"sonar.password", "admin"); | |||
} | |||
} |