123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- /*
- * SonarQube
- * Copyright (C) 2009-2021 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.sonar.server.project.ws;
-
- import com.google.common.collect.ImmutableSet;
- import com.google.common.collect.Lists;
- import java.util.List;
- import java.util.function.Function;
- import javax.annotation.CheckForNull;
- import javax.annotation.Nullable;
- import org.sonar.api.measures.CoreMetrics;
- import org.sonar.api.resources.Qualifiers;
- 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.web.UserRole;
- import org.sonar.db.DatabaseUtils;
- 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.ProjectLinkDto;
- import org.sonar.db.component.SnapshotDto;
- import org.sonar.db.measure.LiveMeasureDto;
- import org.sonar.server.user.UserSession;
- import org.sonarqube.ws.Projects.SearchMyProjectsWsResponse;
- import org.sonarqube.ws.Projects.SearchMyProjectsWsResponse.Link;
- import org.sonarqube.ws.Projects.SearchMyProjectsWsResponse.Project;
-
- import static com.google.common.base.Strings.emptyToNull;
- import static com.google.common.base.Strings.isNullOrEmpty;
- import static java.util.Collections.singletonList;
- import static java.util.Objects.requireNonNull;
- import static java.util.Optional.ofNullable;
- import static org.sonar.api.utils.DateUtils.formatDateTime;
- import static org.sonar.api.utils.Paging.offset;
- import static org.sonar.server.project.ws.SearchMyProjectsData.builder;
- import static org.sonar.server.ws.WsUtils.writeProtobuf;
-
- public class SearchMyProjectsAction implements ProjectsWsAction {
- private static final int MAX_SIZE = 500;
-
- private final DbClient dbClient;
- private final UserSession userSession;
-
- public SearchMyProjectsAction(DbClient dbClient, UserSession userSession) {
- this.dbClient = dbClient;
- this.userSession = userSession;
- }
-
- @Override
- public void define(WebService.NewController context) {
- context.createAction("search_my_projects")
- .setDescription("Return list of projects for which the current user has 'Administer' permission. Maximum 1'000 projects are returned.")
- .setResponseExample(getClass().getResource("search_my_projects-example.json"))
- .addPagingParams(100, MAX_SIZE)
- .setSince("6.0")
- .setInternal(true)
- .setHandler(this);
-
- }
-
- @Override
- public void handle(Request request, Response response) throws Exception {
- SearchMyProjectsWsResponse searchMyProjectsWsResponse = doHandle(toRequest(request));
- writeProtobuf(searchMyProjectsWsResponse, request, response);
- }
-
- private SearchMyProjectsWsResponse doHandle(SearchMyProjectsRequest request) {
- checkAuthenticated();
-
- try (DbSession dbSession = dbClient.openSession(false)) {
- SearchMyProjectsData data = load(dbSession, request);
- return buildResponse(request, data);
- }
- }
-
- private static SearchMyProjectsWsResponse buildResponse(SearchMyProjectsRequest request, SearchMyProjectsData data) {
- SearchMyProjectsWsResponse.Builder response = SearchMyProjectsWsResponse.newBuilder();
-
- ProjectDtoToWs projectDtoToWs = new ProjectDtoToWs(data);
- data.projects().stream()
- .map(projectDtoToWs)
- .forEach(response::addProjects);
-
- response.getPagingBuilder()
- .setPageIndex(request.getPage())
- .setPageSize(request.getPageSize())
- .setTotal(data.totalNbOfProjects())
- .build();
-
- return response.build();
- }
-
- private void checkAuthenticated() {
- userSession.checkLoggedIn();
- }
-
- private static SearchMyProjectsRequest toRequest(Request request) {
- return SearchMyProjectsRequest.builder()
- .setPage(request.mandatoryParamAsInt(Param.PAGE))
- .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE))
- .build();
- }
-
- private static class ProjectDtoToWs implements Function<ComponentDto, Project> {
- private final SearchMyProjectsData data;
-
- private ProjectDtoToWs(SearchMyProjectsData data) {
- this.data = data;
- }
-
- @Override
- public Project apply(ComponentDto dto) {
- Project.Builder project = Project.newBuilder();
- project
- .setKey(dto.getDbKey())
- .setName(dto.name());
- data.lastSnapshot(dto.uuid()).ifPresent(s -> {
- project.setLastAnalysisDate(formatDateTime(s.getCreatedAt()));
- ofNullable(s.getRevision()).ifPresent(project::setRevision);
- });
- data.qualityGateStatusFor(dto.uuid()).ifPresent(project::setQualityGate);
- ofNullable(emptyToNull(dto.description())).ifPresent(project::setDescription);
-
- data.projectLinksFor(dto.uuid()).stream()
- .map(ProjectLinkDtoToWs.INSTANCE)
- .forEach(project::addLinks);
-
- return project.build();
- }
- }
-
- private enum ProjectLinkDtoToWs implements Function<ProjectLinkDto, Link> {
- INSTANCE;
-
- @Override
- public Link apply(ProjectLinkDto dto) {
- Link.Builder link = Link.newBuilder();
- link.setHref(dto.getHref());
-
- if (!isNullOrEmpty(dto.getName())) {
- link.setName(dto.getName());
- }
- if (!isNullOrEmpty(dto.getType())) {
- link.setType(dto.getType());
- }
-
- return link.build();
- }
- }
-
- private SearchMyProjectsData load(DbSession dbSession, SearchMyProjectsRequest request) {
- SearchMyProjectsData.Builder data = builder();
- ProjectsResult searchResult = searchProjects(dbSession, request);
- List<ComponentDto> projects = searchResult.projects;
- List<String> projectUuids = Lists.transform(projects, ComponentDto::projectUuid);
- List<ProjectLinkDto> projectLinks = dbClient.projectLinkDao().selectByProjectUuids(dbSession, projectUuids);
- List<SnapshotDto> snapshots = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, projectUuids);
- List<LiveMeasureDto> qualityGates = dbClient.liveMeasureDao()
- .selectByComponentUuidsAndMetricKeys(dbSession, projectUuids, singletonList(CoreMetrics.ALERT_STATUS_KEY));
-
- data.setProjects(projects)
- .setProjectLinks(projectLinks)
- .setSnapshots(snapshots)
- .setQualityGates(qualityGates)
- .setTotalNbOfProjects(searchResult.total);
-
- return data.build();
- }
-
- private ProjectsResult searchProjects(DbSession dbSession, SearchMyProjectsRequest request) {
- String userUuid = requireNonNull(userSession.getUuid(), "Current user must be authenticated");
-
- List<String> componentUuids = dbClient.roleDao().selectComponentUuidsByPermissionAndUserUuid(dbSession, UserRole.ADMIN, userUuid);
- ComponentQuery dbQuery = ComponentQuery.builder()
- .setQualifiers(Qualifiers.PROJECT)
- .setComponentUuids(ImmutableSet.copyOf(componentUuids.subList(0, Math.min(componentUuids.size(), DatabaseUtils.PARTITION_SIZE_FOR_ORACLE))))
- .build();
-
- return new ProjectsResult(
- dbClient.componentDao().selectByQuery(dbSession, dbQuery, offset(request.getPage(), request.getPageSize()), request.getPageSize()),
- dbClient.componentDao().countByQuery(dbSession, dbQuery));
- }
-
- private static class ProjectsResult {
- private final List<ComponentDto> projects;
- private final int total;
-
- private ProjectsResult(List<ComponentDto> projects, int total) {
- this.projects = projects;
- this.total = total;
- }
- }
-
- private static class SearchMyProjectsRequest {
- private final Integer page;
- private final Integer pageSize;
-
- private SearchMyProjectsRequest(Builder builder) {
- this.page = builder.page;
- this.pageSize = builder.pageSize;
- }
-
- @CheckForNull
- public Integer getPage() {
- return page;
- }
-
- @CheckForNull
- public Integer getPageSize() {
- return pageSize;
- }
-
- public static Builder builder() {
- return new Builder();
- }
- }
-
- private static class Builder {
- private Integer page;
- private Integer pageSize;
-
- private Builder() {
- // enforce method constructor
- }
-
- public Builder setPage(@Nullable Integer page) {
- this.page = page;
- return this;
- }
-
- public Builder setPageSize(@Nullable Integer pageSize) {
- this.pageSize = pageSize;
- return this;
- }
-
- public SearchMyProjectsRequest build() {
- return new SearchMyProjectsRequest(this);
- }
- }
- }
|