private final String nameOrKeyQuery;
private final String[] qualifiers;
private final String language;
+ private final Boolean isPrivate;
private final Set<Long> componentIds;
/**
this.qualifiers = Builder.validateQualifiers(qualifiers);
this.language = null;
this.componentIds = null;
+ this.isPrivate = null;
}
private ComponentQuery(Builder builder) {
this.qualifiers = builder.qualifiers;
this.language = builder.language;
this.componentIds = builder.componentIds;
+ this.isPrivate = builder.isPrivate;
}
public String[] getQualifiers() {
return componentIds;
}
+ @CheckForNull
+ public Boolean getPrivate() {
+ return isPrivate;
+ }
+
public static Builder builder() {
return new Builder();
}
private String nameOrKeyQuery;
private String[] qualifiers;
private String language;
+ private Boolean isPrivate;
private Set<Long> componentIds;
public Builder setNameOrKeyQuery(@Nullable String nameOrKeyQuery) {
return this;
}
+ public Builder setPrivate(@Nullable Boolean isPrivate) {
+ this.isPrivate = isPrivate;
+ return this;
+ }
+
protected static String[] validateQualifiers(@Nullable String... qualifiers) {
checkArgument(qualifiers != null && qualifiers.length > 0, "At least one qualifier must be provided");
return qualifiers;
upper(p.name) like #{query.nameOrKeyUpperLikeQuery,jdbcType=VARCHAR} escape '/'
)
</if>
+ <if test="query.private!=null">
+ <if test="query.private.equals(true)">
+ and p.private=${_true}
+ </if>
+ <if test="query.private.equals(false)">
+ and p.private=${_false}
+ </if>
+ </if>
</sql>
<select id="selectDescendants" resultType="Component">
assertThat(result.get(0).key()).isEqualTo("java-project-key");
}
+ @Test
+ public void selectByQuery_filter_on_visibility() {
+ db.components().insertComponent(ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setKey("private-key"));
+ db.components().insertComponent(ComponentTesting.newPublicProjectDto(db.getDefaultOrganization()).setKey("public-key"));
+
+ ComponentQuery privateProjectsQuery = ComponentQuery.builder().setPrivate(true).setQualifiers(Qualifiers.PROJECT).build();
+ ComponentQuery publicProjectsQuery = ComponentQuery.builder().setPrivate(false).setQualifiers(Qualifiers.PROJECT).build();
+ ComponentQuery allProjectsQuery = ComponentQuery.builder().setPrivate(null).setQualifiers(Qualifiers.PROJECT).build();
+
+ assertThat(underTest.selectByQuery(dbSession, privateProjectsQuery, 0, 10)).extracting(ComponentDto::getKey).containsExactly("private-key");
+ assertThat(underTest.selectByQuery(dbSession, publicProjectsQuery, 0, 10)).extracting(ComponentDto::getKey).containsExactly("public-key");
+ assertThat(underTest.selectByQuery(dbSession, allProjectsQuery, 0, 10)).extracting(ComponentDto::getKey).containsOnly("public-key", "private-key");
+ }
+
@Test
public void selectByQuery_on_empty_list_of_component_id() {
ComponentQuery dbQuery = ComponentQuery.builder().setQualifiers(Qualifiers.PROJECT).setComponentIds(emptySet()).build();
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentQuery;
import org.sonar.db.organization.OrganizationDto;
-import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.db.permission.OrganizationPermission;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+import org.sonar.server.project.Visibility;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.WsProjects.SearchWsResponse;
import org.sonarqube.ws.client.project.SearchWsRequest;
import static java.util.Optional.ofNullable;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.resources.Qualifiers.VIEW;
+import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.server.project.Visibility.PRIVATE;
import static org.sonar.server.project.Visibility.PUBLIC;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.project.ProjectsWsParameters.MAX_PAGE_SIZE;
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;
public class SearchAction implements ProjectsWsAction {
.setPossibleValues(PROJECT, VIEW)
.setDefaultValue(PROJECT);
support.addOrganizationParam(action);
+
+ action.createParam(PARAM_VISIBILITY)
+ .setDescription("Filter the projects that should be visible to everyone (%s), or only specific user/groups (%s).<br/>" +
+ "If no visibility is specified, the default project visibility of the organization will be used.",
+ Visibility.PUBLIC.getLabel(), Visibility.PRIVATE.getLabel())
+ .setRequired(false)
+ .setInternal(true)
+ .setSince("6.4")
+ .setPossibleValues(Visibility.getLabels());
}
@Override
.setQualifiers(request.mandatoryParamAsStrings(PARAM_QUALIFIERS))
.setQuery(request.param(Param.TEXT_QUERY))
.setPage(request.mandatoryParamAsInt(Param.PAGE))
- .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)).build();
+ .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE))
+ .setVisibility(request.param(PARAM_VISIBILITY))
+ .build();
}
private SearchWsResponse doHandle(SearchWsRequest request) {
private static ComponentQuery buildQuery(SearchWsRequest request) {
List<String> qualifiers = request.getQualifiers();
- return ComponentQuery.builder()
+ ComponentQuery.Builder query = ComponentQuery.builder()
.setNameOrKeyQuery(request.getQuery())
- .setQualifiers(qualifiers.toArray(new String[qualifiers.size()]))
- .build();
+ .setQualifiers(qualifiers.toArray(new String[qualifiers.size()]));
+
+ setNullable(request.getVisibility(), v -> query.setPrivate(Visibility.isPrivate(v)));
+
+ return query.build();
}
private Paging buildPaging(DbSession dbSession, SearchWsRequest request, OrganizationDto organization, ComponentQuery query) {
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_ORGANIZATION;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_VISIBILITY;
public class SearchActionTest {
assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly("project-_%-key");
}
+ @Test
+ public void search_private_projects() {
+ userSession.addPermission(ADMINISTER, db.getDefaultOrganization());
+ db.components().insertComponents(
+ ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setKey("private-key"),
+ ComponentTesting.newPublicProjectDto(db.getDefaultOrganization()).setKey("public-key"));
+
+ SearchWsResponse response = call(SearchWsRequest.builder().setVisibility("private").build());
+
+ assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly("private-key");
+ }
+
+ @Test
+ public void search_public_projects() {
+ userSession.addPermission(ADMINISTER, db.getDefaultOrganization());
+ db.components().insertComponents(
+ ComponentTesting.newPrivateProjectDto(db.getDefaultOrganization()).setKey("private-key"),
+ ComponentTesting.newPublicProjectDto(db.getDefaultOrganization()).setKey("public-key"));
+
+ SearchWsResponse response = call(SearchWsRequest.builder().setVisibility("public").build());
+
+ assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly("public-key");
+ }
+
@Test
public void search_projects_when_no_qualifier_set() throws IOException {
userSession.addPermission(ADMINISTER, db.getDefaultOrganization());
assertThat(action.isInternal()).isTrue();
assertThat(action.since()).isEqualTo("6.3");
assertThat(action.handler()).isEqualTo(ws.getDef().handler());
- assertThat(action.params()).hasSize(5);
+ assertThat(action.params()).hasSize(6);
assertThat(action.responseExample()).isEqualTo(getClass().getResource("search-example.json"));
WebService.Param organization = action.param("organization");
assertThat(psParam.isRequired()).isFalse();
assertThat(psParam.defaultValue()).isEqualTo("100");
assertThat(psParam.description()).isEqualTo("Page size. Must be greater than 0 and less than 500");
+
+ WebService.Param visibilityParam = action.param("visibility");
+ 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.");
}
@Test
setNullable(wsRequest.getQuery(), query -> request.setParam(TEXT_QUERY, query));
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));
return request.executeProtobuf(SearchWsResponse.class);
}
private final String organization;
private final String query;
private final List<String> qualifiers;
+ private final String visibility;
private final Integer page;
private final Integer pageSize;
this.organization = builder.organization;
this.query = builder.query;
this.qualifiers = builder.qualifiers;
+ this.visibility = builder.visibility;
this.page = builder.page;
this.pageSize = builder.pageSize;
}
return query;
}
+ @CheckForNull
+ public String getVisibility() {
+ return visibility;
+ }
+
public static Builder builder() {
return new Builder();
}
private Integer page;
private Integer pageSize;
private String query;
+ private String visibility;
public Builder setOrganization(@Nullable String organization) {
this.organization = organization;
return this;
}
+ public Builder setVisibility(@Nullable String visibility) {
+ this.visibility = visibility;
+ 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);