return checkFoundWithOptional(organizationDto, "Organization with uuid '%s' not found", organizationUuid);
}
- /**
- * Components of the main branch won't be found
- */
public ComponentDto getByKeyAndBranch(DbSession dbSession, String key, String branch) {
Optional<ComponentDto> componentDto = dbClient.componentDao().selectByKeyAndBranch(dbSession, key, branch);
if (componentDto.isPresent() && componentDto.get().isEnabled()) {
import org.sonar.db.measure.LiveMeasureDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.component.ComponentFinder.ParamNames;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.user.UserSession;
import org.sonar.server.ws.KeyExamples;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.ACTION_PROJECT_STATUS;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_ANALYSIS_ID;
+import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_BRANCH;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PROJECT_ID;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PROJECT_KEY;
+import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
import static org.sonar.server.ws.WsUtils.checkRequest;
private static final String QG_STATUSES_ONE_LINE = Arrays.stream(ProjectStatusResponse.Status.values())
.map(Enum::toString)
.collect(Collectors.joining(", "));
- private static final String MSG_ONE_PARAMETER_ONLY = String.format("Either '%s', '%s' or '%s' must be provided", PARAM_ANALYSIS_ID, PARAM_PROJECT_ID, PARAM_PROJECT_KEY);
+ private static final String MSG_ONE_PROJECT_PARAMETER_ONLY = String.format("Either '%s', '%s' or '%s' must be provided", PARAM_ANALYSIS_ID, PARAM_PROJECT_ID, PARAM_PROJECT_KEY);
+ private static final String MSG_ONE_BRANCH_PARAMETER_ONLY = String.format("Either '%s' or '%s' can be provided, not both", PARAM_BRANCH, PARAM_PULL_REQUEST);
private final DbClient dbClient;
private final ComponentFinder componentFinder;
public void define(WebService.NewController controller) {
WebService.NewAction action = controller.createAction(ACTION_PROJECT_STATUS)
.setDescription(String.format("Get the quality gate status of a project or a Compute Engine task.<br />" +
- MSG_ONE_PARAMETER_ONLY + "<br />" +
+ MSG_ONE_PROJECT_PARAMETER_ONLY + "<br />" +
"The different statuses returned are: %s. The %s status is returned when there is no quality gate associated with the analysis.<br />" +
"Returns an HTTP code 404 if the analysis associated with the task is not found or does not exist.<br />" +
"Requires one of the following permissions:" +
.setSince("5.3")
.setHandler(this)
.setChangelog(
+ new Change("7.7", "The parameters 'branch' and 'pullRequest' were added"),
new Change("7.6", "The field 'warning' is deprecated from the response"),
new Change("6.4", "The field 'ignoredConditions' is added to the response"));
action.createParam(PARAM_PROJECT_ID)
.setSince("5.4")
- .setDescription("Project id")
+ .setDescription("Project id. Doesn't work with branches or pull requests")
.setExampleValue(Uuids.UUID_EXAMPLE_01);
action.createParam(PARAM_PROJECT_KEY)
.setSince("5.4")
.setDescription("Project key")
.setExampleValue(KeyExamples.KEY_PROJECT_EXAMPLE_001);
+
+ action.createParam(PARAM_BRANCH)
+ .setSince("7.7")
+ .setDescription("Branch key")
+ .setExampleValue(KeyExamples.KEY_BRANCH_EXAMPLE_001);
+
+ action.createParam(PARAM_PULL_REQUEST)
+ .setSince("7.7")
+ .setDescription("Pull request id")
+ .setExampleValue(KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001);
}
@Override
String analysisId = request.param(PARAM_ANALYSIS_ID);
String projectId = request.param(PARAM_PROJECT_ID);
String projectKey = request.param(PARAM_PROJECT_KEY);
+ String branchKey = request.param(PARAM_BRANCH);
+ String pullRequestId = request.param(PARAM_PULL_REQUEST);
checkRequest(
!isNullOrEmpty(analysisId)
^ !isNullOrEmpty(projectId)
^ !isNullOrEmpty(projectKey),
- MSG_ONE_PARAMETER_ONLY);
+ MSG_ONE_PROJECT_PARAMETER_ONLY);
+ checkRequest(isNullOrEmpty(branchKey) || isNullOrEmpty(pullRequestId), MSG_ONE_BRANCH_PARAMETER_ONLY);
try (DbSession dbSession = dbClient.openSession(false)) {
- ProjectStatusResponse projectStatusResponse = doHandle(dbSession, analysisId, projectId, projectKey);
+ ProjectStatusResponse projectStatusResponse = doHandle(dbSession, analysisId, projectId, projectKey, branchKey, pullRequestId);
writeProtobuf(projectStatusResponse, request, response);
}
}
- private ProjectStatusResponse doHandle(DbSession dbSession, @Nullable String analysisId, @Nullable String projectId, @Nullable String projectKey) {
- ProjectAndSnapshot projectAndSnapshot = getProjectAndSnapshot(dbSession, analysisId, projectId, projectKey);
+ private ProjectStatusResponse doHandle(DbSession dbSession, @Nullable String analysisId, @Nullable String projectId,
+ @Nullable String projectKey, @Nullable String branchKey, @Nullable String pullRequestId) {
+ ProjectAndSnapshot projectAndSnapshot = getProjectAndSnapshot(dbSession, analysisId, projectId, projectKey, branchKey, pullRequestId);
checkPermission(projectAndSnapshot.project);
Optional<String> measureData = loadQualityGateDetails(dbSession, projectAndSnapshot, analysisId != null);
.build();
}
- private ProjectAndSnapshot getProjectAndSnapshot(DbSession dbSession, @Nullable String analysisId, @Nullable String projectId, @Nullable String projectKey) {
+ private ProjectAndSnapshot getProjectAndSnapshot(DbSession dbSession, @Nullable String analysisId, @Nullable String projectId,
+ @Nullable String projectKey, @Nullable String branchKey, @Nullable String pullRequestId) {
if (!isNullOrEmpty(analysisId)) {
return getSnapshotThenProject(dbSession, analysisId);
}
if (!isNullOrEmpty(projectId) ^ !isNullOrEmpty(projectKey)) {
- return getProjectThenSnapshot(dbSession, projectId, projectKey);
+ return getProjectThenSnapshot(dbSession, projectId, projectKey, branchKey, pullRequestId);
}
- throw BadRequestException.create(MSG_ONE_PARAMETER_ONLY);
+ throw BadRequestException.create(MSG_ONE_PROJECT_PARAMETER_ONLY);
}
- private ProjectAndSnapshot getProjectThenSnapshot(DbSession dbSession, String projectId, String projectKey) {
- ComponentDto projectDto = componentFinder.getByUuidOrKey(dbSession, projectId, projectKey, ParamNames.PROJECT_ID_AND_KEY);
+ private ProjectAndSnapshot getProjectThenSnapshot(DbSession dbSession, @Nullable String projectId, @Nullable String projectKey,
+ @Nullable String branchKey, @Nullable String pullRequestId) {
+ ComponentDto projectDto;
+ if (projectId != null) {
+ projectDto = componentFinder.getRootComponentByUuidOrKey(dbSession, projectId, null);
+ } else {
+ projectDto = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branchKey, pullRequestId);
+ }
Optional<SnapshotDto> snapshot = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, projectDto.projectUuid());
return new ProjectAndSnapshot(projectDto, snapshot.orElse(null));
}
static final String PARAM_ORGANIZATION = "organization";
public static final String PARAM_ANALYSIS_ID = "analysisId";
+ public static final String PARAM_BRANCH = "branch";
+ public static final String PARAM_PULL_REQUEST = "pullRequest";
public static final String PARAM_PROJECT_ID = "projectId";
public static final String PARAM_PROJECT_KEY = "projectKey";
public static final String PARAM_PAGE_SIZE = "pageSize";
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.metric.MetricDto;
import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
import static org.sonar.db.metric.MetricTesting.newMetricDto;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_ANALYSIS_ID;
+import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_BRANCH;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_ORGANIZATION;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PROJECT_ID;
import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PROJECT_KEY;
+import static org.sonar.server.qualitygate.ws.QualityGatesWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.test.JsonAssert.assertJson;
public class ProjectStatusActionTest {
.containsExactlyInAnyOrder(
tuple("analysisId", false),
tuple("projectKey", false),
- tuple("projectId", false));
+ tuple("projectId", false),
+ tuple("branch", false),
+ tuple("pullRequest", false));
}
@Test
assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
}
+ @Test
+ public void return_past_status_when_branch_is_referenced_by_past_analysis_id() throws IOException {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ ComponentDto branch = db.components().insertProjectBranch(project);
+ SnapshotDto pastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(branch)
+ .setLast(false)
+ .setPeriodMode("last_version")
+ .setPeriodParam("2015-12-07")
+ .setPeriodDate(956789123987L));
+ SnapshotDto lastAnalysis = dbClient.snapshotDao().insert(dbSession, newAnalysis(branch)
+ .setLast(true)
+ .setPeriodMode("last_version")
+ .setPeriodParam("2016-12-07")
+ .setPeriodDate(1_500L));
+ MetricDto gateDetailsMetric = insertGateDetailMetric();
+ dbClient.measureDao().insert(dbSession,
+ newMeasureDto(gateDetailsMetric, branch, pastAnalysis)
+ .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionTest/measure_data.json"))));
+ dbClient.measureDao().insert(dbSession,
+ newMeasureDto(gateDetailsMetric, branch, lastAnalysis)
+ .setData("not_used"));
+ dbSession.commit();
+ userSession.addProjectPermission(UserRole.USER, project);
+
+ String response = ws.newRequest()
+ .setParam(PARAM_ANALYSIS_ID, pastAnalysis.getUuid())
+ .execute().getInput();
+
+ assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
+ }
+
@Test
public void return_live_status_when_project_is_referenced_by_its_key() throws IOException {
OrganizationDto organization = db.organizations().insert();
assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
}
+ @Test
+ public void return_live_status_when_branch_is_referenced_by_its_key() throws IOException {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ ComponentDto branch = db.components().insertProjectBranch(project);
+
+ dbClient.snapshotDao().insert(dbSession, newAnalysis(branch)
+ .setPeriodMode("last_version")
+ .setPeriodParam("2015-12-07")
+ .setPeriodDate(956789123987L));
+ MetricDto gateDetailsMetric = insertGateDetailMetric();
+ dbClient.liveMeasureDao().insert(dbSession,
+ newLiveMeasure(branch, gateDetailsMetric)
+ .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionTest/measure_data.json"))));
+ dbSession.commit();
+ userSession.addProjectPermission(UserRole.USER, project);
+
+ String response = ws.newRequest()
+ .setParam(PARAM_PROJECT_KEY, project.getKey())
+ .setParam(PARAM_BRANCH, branch.getBranch())
+ .execute().getInput();
+
+ assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
+ }
+
+ @Test
+ public void return_live_status_when_pull_request_is_referenced_by_its_key() throws IOException {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ ComponentDto pr = db.components().insertProjectBranch(project, branch -> branch.setBranchType(BranchType.PULL_REQUEST));
+
+ dbClient.snapshotDao().insert(dbSession, newAnalysis(pr)
+ .setPeriodMode("last_version")
+ .setPeriodParam("2015-12-07")
+ .setPeriodDate(956789123987L));
+ MetricDto gateDetailsMetric = insertGateDetailMetric();
+ dbClient.liveMeasureDao().insert(dbSession,
+ newLiveMeasure(pr, gateDetailsMetric)
+ .setData(IOUtils.toString(getClass().getResource("ProjectStatusActionTest/measure_data.json"))));
+ dbSession.commit();
+ userSession.addProjectPermission(UserRole.USER, project);
+
+ String response = ws.newRequest()
+ .setParam(PARAM_PROJECT_KEY, project.getKey())
+ .setParam(PARAM_PULL_REQUEST, pr.getPullRequest())
+ .execute().getInput();
+
+ assertJson(response).isSimilarTo(getClass().getResource("project_status-example.json"));
+ }
+
@Test
public void return_undefined_status_if_specified_analysis_is_not_found() {
OrganizationDto organization = db.organizations().insert();
.execute().getInput();
}
+ @Test
+ public void fail_if_branch_key_and_pull_request_id_provided() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ logInAsSystemAdministrator();
+
+ expectedException.expect(BadRequestException.class);
+ expectedException.expectMessage("Either 'branch' or 'pullRequest' can be provided, not both");
+
+ ws.newRequest()
+ .setParam(PARAM_PROJECT_KEY, "key")
+ .setParam(PARAM_BRANCH, "branch")
+ .setParam(PARAM_PULL_REQUEST, "pr")
+ .setParam(PARAM_ORGANIZATION, organization.getKey())
+ .execute().getInput();
+ }
+
@Test
public void fail_if_no_parameter_provided() {
logInAsSystemAdministrator();
SnapshotDto snapshot = db.components().insertSnapshot(branch);
expectedException.expect(NotFoundException.class);
- expectedException.expectMessage(format("Component id '%s' not found", branch.uuid()));
+ expectedException.expectMessage(format("Project id '%s' not found", branch.uuid()));
ws.newRequest()
.setParam("projectId", branch.uuid())
private String analysisId;
private String projectId;
private String projectKey;
+ private String branch;
+ private String pullRequest;
/**
* Example value: "AU-TpxcA-iU5OvuD2FL1"
public String getProjectKey() {
return projectKey;
}
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public ProjectStatusRequest setBranch(String branch) {
+ this.branch = branch;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ public ProjectStatusRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
}
*/
package org.sonarqube.ws.client.qualitygates;
-import java.util.stream.Collectors;
import javax.annotation.Generated;
import org.sonarqube.ws.MediaTypes;
-import org.sonarqube.ws.client.BaseService;
-import org.sonarqube.ws.client.GetRequest;
-import org.sonarqube.ws.client.PostRequest;
-import org.sonarqube.ws.client.WsConnector;
-import org.sonarqube.ws.Qualitygates.CreateResponse;
import org.sonarqube.ws.Qualitygates.CreateConditionResponse;
+import org.sonarqube.ws.Qualitygates.CreateResponse;
import org.sonarqube.ws.Qualitygates.GetByProjectResponse;
import org.sonarqube.ws.Qualitygates.ListWsResponse;
import org.sonarqube.ws.Qualitygates.ProjectStatusResponse;
import org.sonarqube.ws.Qualitygates.SearchResponse;
import org.sonarqube.ws.Qualitygates.ShowWsResponse;
import org.sonarqube.ws.Qualitygates.UpdateConditionResponse;
+import org.sonarqube.ws.client.BaseService;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.WsConnector;
/**
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates">Further information about this web service online</a>
}
/**
- *
* This is part of the internal API.
* This is a POST request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/copy">Further information about this action online (including a response example)</a>
* @since 4.3
*/
.setParam("name", request.getName())
.setParam("organization", request.getOrganization())
.setMediaType(MediaTypes.JSON)
- ).content();
+ ).content();
}
/**
- *
* This is part of the internal API.
* This is a POST request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/create">Further information about this action online (including a response example)</a>
* @since 4.3
*/
}
/**
- *
* This is part of the internal API.
* This is a POST request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/create_condition">Further information about this action online (including a response example)</a>
* @since 4.3
*/
}
/**
- *
* This is part of the internal API.
* This is a POST request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/delete_condition">Further information about this action online (including a response example)</a>
* @since 4.3
*/
.setParam("id", request.getId())
.setParam("organization", request.getOrganization())
.setMediaType(MediaTypes.JSON)
- ).content();
+ ).content();
}
/**
- *
* This is part of the internal API.
* This is a POST request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/deselect">Further information about this action online (including a response example)</a>
* @since 4.3
*/
.setParam("projectId", request.getProjectId())
.setParam("projectKey", request.getProjectKey())
.setMediaType(MediaTypes.JSON)
- ).content();
+ ).content();
}
/**
- *
* This is part of the internal API.
* This is a POST request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/destroy">Further information about this action online (including a response example)</a>
* @since 4.3
*/
.setParam("id", request.getId())
.setParam("organization", request.getOrganization())
.setMediaType(MediaTypes.JSON)
- ).content();
+ ).content();
}
/**
- *
* This is part of the internal API.
* This is a GET request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/get_by_project">Further information about this action online (including a response example)</a>
* @since 6.1
*/
}
/**
- *
* This is part of the internal API.
* This is a GET request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/list">Further information about this action online (including a response example)</a>
* @since 4.3
*/
}
/**
- *
* This is part of the internal API.
* This is a GET request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/project_status">Further information about this action online (including a response example)</a>
* @since 5.3
*/
new GetRequest(path("project_status"))
.setParam("analysisId", request.getAnalysisId())
.setParam("projectId", request.getProjectId())
- .setParam("projectKey", request.getProjectKey()),
+ .setParam("projectKey", request.getProjectKey())
+ .setParam("branch", request.getBranch())
+ .setParam("pullRequest", request.getPullRequest()),
ProjectStatusResponse.parser());
}
/**
- *
* This is part of the internal API.
* This is a POST request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/rename">Further information about this action online (including a response example)</a>
* @since 4.3
*/
.setParam("name", request.getName())
.setParam("organization", request.getOrganization())
.setMediaType(MediaTypes.JSON)
- ).content();
+ ).content();
}
/**
- *
* This is part of the internal API.
* This is a GET request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/search">Further information about this action online (including a response example)</a>
* @since 4.3
*/
}
/**
- *
* This is part of the internal API.
* This is a POST request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/select">Further information about this action online (including a response example)</a>
* @since 4.3
*/
.setParam("projectId", request.getProjectId())
.setParam("projectKey", request.getProjectKey())
.setMediaType(MediaTypes.JSON)
- ).content();
+ ).content();
}
/**
- *
* This is part of the internal API.
* This is a POST request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/set_as_default">Further information about this action online (including a response example)</a>
* @since 4.3
*/
.setParam("id", request.getId())
.setParam("organization", request.getOrganization())
.setMediaType(MediaTypes.JSON)
- ).content();
+ ).content();
}
/**
- *
* This is part of the internal API.
* This is a GET request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/show">Further information about this action online (including a response example)</a>
* @since 4.3
*/
}
/**
- *
* This is part of the internal API.
* This is a POST request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/unset_default">Further information about this action online (including a response example)</a>
* @since 4.3
* @deprecated since 7.0
return call(
new PostRequest(path("unset_default"))
.setMediaType(MediaTypes.JSON)
- ).content();
+ ).content();
}
/**
- *
* This is part of the internal API.
* This is a POST request.
+ *
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates/update_condition">Further information about this action online (including a response example)</a>
* @since 4.3
*/