SONAR-10366 Add pull request object to api/project_branches/list and api/ce/activity SONAR-10365 Analyze pull requests as 1st class citizen SONAR-10366 SONAR-10367 Create WS api/projet_pull_requests/list and delete SONAR-10383 Add Pull Request information when listing CE tasks SONAR-10365 Add key type in PROJECT_BRANCHES (#3063) SONAR-10371 Add pullRequest parameter in the Web API (#3076) * ComponentFinder searches by branch or pull request * Add pullRequest parameter to WS api/issues/* WS * Add pullRequest parameter to api/settings/* WS * Add pullRequest parameter to api/badges/* WS * Add pullRequest parameter to api/components/* WS * Add pullRequest parameter to api/sources/* WS SONAR-10368 Copy issue states from pull request after it's merged SONAR-10373 Send notifications for events on issues of a pull request SONAR-10365 Add pull_request_binary column in project_branches (#3073) SONAR-10433 Store pull request in projects table SONAR-10371 Add pullRequest field in the Web API SONAR-10365 Analyze pull requests as 1st class citizen BRANCH-45 Expose issue resolution for PR decoration BRANCH-49 Basic support of pull request analysis on pull request branch BRANCH-47 Fail when user tries to analyze a pull request and the plugin is not available SONAR-10366 update pull request decorated links to project and issues SONAR-10365 Use pull request id as key instead of branch name SONAR-10454 Update embedded Git 1.4 and SVN 1.7 SONAR-10365 rename sonar.pullrequest.id to sonar.pullrequest.key SONAR-10383 api/navigation/component returns the pull request keytags/7.5
@@ -120,7 +120,7 @@ public class ComputeEngineContainerImplTest { | |||
+ 26 // level 1 | |||
+ 53 // content of DaoModule | |||
+ 3 // content of EsModule | |||
+ 57 // content of CorePropertyDefinitions | |||
+ 59 // content of CorePropertyDefinitions | |||
+ 1 // StopFlagContainer | |||
); | |||
assertThat( |
@@ -733,13 +733,15 @@ CREATE TABLE "PROJECT_BRANCHES" ( | |||
"UUID" VARCHAR(50) NOT NULL PRIMARY KEY, | |||
"PROJECT_UUID" VARCHAR(50) NOT NULL, | |||
"KEE" VARCHAR(255) NOT NULL, | |||
"BRANCH_TYPE" VARCHAR(5), | |||
"KEY_TYPE" VARCHAR(12) NOT NULL, | |||
"BRANCH_TYPE" VARCHAR(12), | |||
"MERGE_BRANCH_UUID" VARCHAR(50), | |||
"PULL_REQUEST_BINARY" BLOB, | |||
"CREATED_AT" BIGINT NOT NULL, | |||
"UPDATED_AT" BIGINT NOT NULL | |||
); | |||
CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID"); | |||
CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE"); | |||
CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE_KEY_TYPE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE", "KEY_TYPE"); | |||
CREATE TABLE "ANALYSIS_PROPERTIES" ( | |||
"UUID" VARCHAR(40) NOT NULL PRIMARY KEY, |
@@ -23,6 +23,7 @@ public class CeTaskCharacteristicDto { | |||
public static final String BRANCH_KEY = "branch"; | |||
public static final String BRANCH_TYPE_KEY = "branchType"; | |||
public static final String PULL_REQUEST = "pullRequest"; | |||
private String uuid; | |||
private String taskUuid; |
@@ -37,24 +37,42 @@ public class BranchDao implements Dao { | |||
} | |||
public void insert(DbSession dbSession, BranchDto dto) { | |||
setKeyType(dto); | |||
mapper(dbSession).insert(dto, system2.now()); | |||
} | |||
public void upsert(DbSession dbSession, BranchDto dto) { | |||
BranchMapper mapper = mapper(dbSession); | |||
long now = system2.now(); | |||
setKeyType(dto); | |||
if (mapper.update(dto, now) == 0) { | |||
mapper.insert(dto, now); | |||
} | |||
} | |||
private static void setKeyType(BranchDto dto) { | |||
if (dto.getBranchType() == BranchType.PULL_REQUEST) { | |||
dto.setKeyType(KeyType.PULL_REQUEST); | |||
} else { | |||
dto.setKeyType(KeyType.BRANCH); | |||
} | |||
} | |||
public int updateMainBranchName(DbSession dbSession, String projectUuid, String newBranchKey) { | |||
long now = system2.now(); | |||
return mapper(dbSession).updateMainBranchName(projectUuid, newBranchKey, now); | |||
} | |||
public Optional<BranchDto> selectByKey(DbSession dbSession, String projectUuid, String key) { | |||
return Optional.ofNullable(mapper(dbSession).selectByKey(projectUuid, key)); | |||
public Optional<BranchDto> selectByBranchKey(DbSession dbSession, String projectUuid, String key) { | |||
return selectByKey(dbSession, projectUuid, key, KeyType.BRANCH); | |||
} | |||
public Optional<BranchDto> selectByPullRequestKey(DbSession dbSession, String projectUuid, String key) { | |||
return selectByKey(dbSession, projectUuid, key, KeyType.PULL_REQUEST); | |||
} | |||
private static Optional<BranchDto> selectByKey(DbSession dbSession, String projectUuid, String key, KeyType keyType) { | |||
return Optional.ofNullable(mapper(dbSession).selectByKey(projectUuid, key, keyType)); | |||
} | |||
public Collection<BranchDto> selectByComponent(DbSession dbSession, ComponentDto component) { |
@@ -19,8 +19,13 @@ | |||
*/ | |||
package org.sonar.db.component; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.util.Objects; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.sonar.db.protobuf.DbProjectBranches; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
@@ -49,10 +54,18 @@ public class BranchDto { | |||
private String projectUuid; | |||
/** | |||
* Name of branch, for example "feature/foo". | |||
* Key that identifies a branch or a pull request. | |||
* For keyType=BRANCH, this is the name of the branch, for example "feature/foo". | |||
* For keyType=PULL_REQUEST, this is the ID of the pull request in some external system, for example 123 in GitHub. | |||
*/ | |||
private String kee; | |||
/** | |||
* Key type, as provided by {@link KeyType}. | |||
* Not null. | |||
*/ | |||
private KeyType keyType; | |||
/** | |||
* Branch type, as provided by {@link BranchType}. | |||
* Not null. | |||
@@ -69,6 +82,12 @@ public class BranchDto { | |||
@Nullable | |||
private String mergeBranchUuid; | |||
/** | |||
* Pull Request data, such as branch name, title, url, and provider specific attributes | |||
*/ | |||
@Nullable | |||
private byte[] pullRequestBinary; | |||
public String getUuid() { | |||
return uuid; | |||
} | |||
@@ -115,6 +134,11 @@ public class BranchDto { | |||
return this; | |||
} | |||
BranchDto setKeyType(@Nullable KeyType keyType) { | |||
this.keyType = keyType; | |||
return this; | |||
} | |||
public BranchType getBranchType() { | |||
return branchType; | |||
} | |||
@@ -134,6 +158,37 @@ public class BranchDto { | |||
return this; | |||
} | |||
public BranchDto setPullRequestData(DbProjectBranches.PullRequestData pullRequestData) { | |||
this.pullRequestBinary = encodePullRequestData(pullRequestData); | |||
return this; | |||
} | |||
@CheckForNull | |||
public DbProjectBranches.PullRequestData getPullRequestData() { | |||
if (pullRequestBinary == null) { | |||
return null; | |||
} | |||
return decodePullRequestData(pullRequestBinary); | |||
} | |||
private static byte[] encodePullRequestData(DbProjectBranches.PullRequestData pullRequestData) { | |||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | |||
try { | |||
pullRequestData.writeTo(outputStream); | |||
return outputStream.toByteArray(); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Fail to serialize pull request data", e); | |||
} | |||
} | |||
private static DbProjectBranches.PullRequestData decodePullRequestData(byte[] pullRequestBinary) { | |||
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(pullRequestBinary)) { | |||
return DbProjectBranches.PullRequestData.parseFrom(inputStream); | |||
} catch (IOException e) { | |||
throw new IllegalStateException("Fail to deserialize pull request data", e); | |||
} | |||
} | |||
@Override | |||
public boolean equals(Object o) { | |||
if (this == o) { | |||
@@ -161,6 +216,7 @@ public class BranchDto { | |||
sb.append("uuid='").append(uuid).append('\''); | |||
sb.append(", projectUuid='").append(projectUuid).append('\''); | |||
sb.append(", kee='").append(kee).append('\''); | |||
sb.append(", keyType=").append(keyType); | |||
sb.append(", branchType=").append(branchType); | |||
sb.append(", mergeBranchUuid='").append(mergeBranchUuid).append('\''); | |||
sb.append('}'); |
@@ -31,7 +31,7 @@ public interface BranchMapper { | |||
int updateMainBranchName(@Param("projectUuid") String projectUuid, @Param("newBranchName") String newBranchName, @Param("now") long now); | |||
BranchDto selectByKey(@Param("projectUuid") String projectUuid, @Param("key") String key); | |||
BranchDto selectByKey(@Param("projectUuid") String projectUuid, @Param("key") String key, @Param("keyType") KeyType keyType); | |||
BranchDto selectByUuid(@Param("uuid") String uuid); | |||
@@ -29,5 +29,10 @@ public enum BranchType { | |||
/** | |||
* Short-lived branch | |||
*/ | |||
SHORT | |||
SHORT, | |||
/** | |||
* Pull request | |||
*/ | |||
PULL_REQUEST | |||
} |
@@ -50,6 +50,7 @@ import static org.sonar.db.DatabaseUtils.executeLargeInputs; | |||
import static org.sonar.db.DatabaseUtils.executeLargeUpdates; | |||
import static org.sonar.db.WildcardPosition.BEFORE_AND_AFTER; | |||
import static org.sonar.db.component.ComponentDto.generateBranchKey; | |||
import static org.sonar.db.component.ComponentDto.generatePullRequestKey; | |||
public class ComponentDao implements Dao { | |||
@@ -201,6 +202,12 @@ public class ComponentDao implements Dao { | |||
return executeLargeInputs(allKeys, subKeys -> mapper(session).selectByKeysAndBranch(subKeys, branch)); | |||
} | |||
public List<ComponentDto> selectByKeysAndPullRequest(DbSession session, Collection<String> keys, String pullRequestId) { | |||
List<String> dbKeys = keys.stream().map(k -> generatePullRequestKey(k, pullRequestId)).collect(toList()); | |||
List<String> allKeys = Stream.of(keys, dbKeys).flatMap(Collection::stream).collect(toList()); | |||
return executeLargeInputs(allKeys, subKeys -> mapper(session).selectByKeysAndBranch(subKeys, pullRequestId)); | |||
} | |||
public List<ComponentDto> selectComponentsHavingSameKeyOrderedById(DbSession session, String key) { | |||
return mapper(session).selectComponentsHavingSameKeyOrderedById(key); | |||
} | |||
@@ -247,7 +254,11 @@ public class ComponentDao implements Dao { | |||
} | |||
public java.util.Optional<ComponentDto> selectByKeyAndBranch(DbSession session, String key, String branch) { | |||
return java.util.Optional.ofNullable(mapper(session).selectByKeyAndBranch(key, generateBranchKey(key, branch), branch)); | |||
return java.util.Optional.ofNullable(mapper(session).selectByKeyAndBranchKey(key, generateBranchKey(key, branch), branch)); | |||
} | |||
public java.util.Optional<ComponentDto> selectByKeyAndPullRequest(DbSession session, String key, String pullRequestId) { | |||
return java.util.Optional.ofNullable(mapper(session).selectByKeyAndBranchKey(key, generatePullRequestKey(key, pullRequestId), pullRequestId)); | |||
} | |||
public List<UuidWithProjectUuidDto> selectAllViewsAndSubViews(DbSession session) { |
@@ -24,15 +24,16 @@ import com.google.common.base.Splitter; | |||
import com.google.common.base.Strings; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.regex.Pattern; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.apache.commons.lang.builder.ToStringBuilder; | |||
import org.sonar.api.resources.Scopes; | |||
import org.sonar.db.WildcardPosition; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static java.lang.String.format; | |||
import static org.apache.commons.lang.StringUtils.substringBeforeLast; | |||
import static org.sonar.db.DaoDatabaseUtils.buildLikeValue; | |||
import static org.sonar.db.component.ComponentValidator.checkComponentKey; | |||
import static org.sonar.db.component.ComponentValidator.checkComponentName; | |||
@@ -44,8 +45,11 @@ public class ComponentDto { | |||
* Separator used to generate the key of the branch | |||
*/ | |||
public static final String BRANCH_KEY_SEPARATOR = ":BRANCH:"; | |||
public static final String PULL_REQUEST_SEPARATOR = ":PULL_REQUEST:"; | |||
private static final Splitter BRANCH_OR_PULL_REQUEST_SPLITTER = Splitter.on(Pattern.compile(BRANCH_KEY_SEPARATOR + "|" + PULL_REQUEST_SEPARATOR)); | |||
private static final Splitter BRANCH_KEY_SPLITTER = Splitter.on(BRANCH_KEY_SEPARATOR); | |||
private static final Splitter PULL_REQUEST_SPLITTER = Splitter.on(PULL_REQUEST_SEPARATOR); | |||
public static final String UUID_PATH_SEPARATOR = "."; | |||
public static final String UUID_PATH_OF_ROOT = UUID_PATH_SEPARATOR; | |||
@@ -65,7 +69,7 @@ public class ComponentDto { | |||
private String organizationUuid; | |||
/** | |||
* Non-empty and unique functional key | |||
* Non-empty and unique functional key. Do not rename, used by MyBatis. | |||
*/ | |||
private String kee; | |||
@@ -206,20 +210,6 @@ public class ComponentDto { | |||
return UUID_PATH_SPLITTER.splitToList(uuidPath); | |||
} | |||
/** | |||
* Used my MyBatis mapper | |||
*/ | |||
private String getKee(){ | |||
return kee; | |||
} | |||
/** | |||
* Used my MyBatis mapper | |||
*/ | |||
private void setKee(String kee){ | |||
this.kee = kee; | |||
} | |||
public String getDbKey() { | |||
return kee; | |||
} | |||
@@ -233,7 +223,7 @@ public class ComponentDto { | |||
* The key to be displayed to user, doesn't contain information on branches | |||
*/ | |||
public String getKey() { | |||
List<String> split = BRANCH_KEY_SPLITTER.splitToList(kee); | |||
List<String> split = BRANCH_OR_PULL_REQUEST_SPLITTER.splitToList(kee); | |||
return split.size() == 2 ? split.get(0) : kee; | |||
} | |||
@@ -246,6 +236,15 @@ public class ComponentDto { | |||
return split.size() == 2 ? split.get(1) : null; | |||
} | |||
/** | |||
* @return the pull request id. It will be null when the component is not on a pull request | |||
*/ | |||
@CheckForNull | |||
public String getPullRequest() { | |||
List<String> split = PULL_REQUEST_SPLITTER.splitToList(kee); | |||
return split.size() == 2 ? split.get(1) : null; | |||
} | |||
public String scope() { | |||
return scope; | |||
} | |||
@@ -525,8 +524,11 @@ public class ComponentDto { | |||
return format("%s%s%s", componentKey, BRANCH_KEY_SEPARATOR, branch); | |||
} | |||
public static String removeBranchFromKey(String componentKey) { | |||
return StringUtils.substringBeforeLast(componentKey, ComponentDto.BRANCH_KEY_SEPARATOR); | |||
public static String generatePullRequestKey(String componentKey, String pullRequest) { | |||
return format("%s%s%s", componentKey, PULL_REQUEST_SEPARATOR, pullRequest); | |||
} | |||
public static String removeBranchAndPullRequestFromKey(String componentKey) { | |||
return substringBeforeLast(substringBeforeLast(componentKey, ComponentDto.BRANCH_KEY_SEPARATOR), ComponentDto.PULL_REQUEST_SEPARATOR); | |||
} | |||
} |
@@ -141,10 +141,14 @@ public class ComponentKeyUpdaterDao implements Dao { | |||
private static String branchBaseKey(String key) { | |||
int index = key.lastIndexOf(ComponentDto.BRANCH_KEY_SEPARATOR); | |||
if (index == -1) { | |||
return key; | |||
if (index > -1) { | |||
return key.substring(0, index); | |||
} | |||
return key.substring(0, index); | |||
index = key.lastIndexOf(ComponentDto.PULL_REQUEST_SEPARATOR); | |||
if (index > -1) { | |||
return key.substring(0, index); | |||
} | |||
return key; | |||
} | |||
private static void runBatchUpdateForAllResources(Collection<ResourceDto> resources, String oldKey, String newKey, ComponentKeyUpdaterMapper mapper) { |
@@ -33,7 +33,7 @@ public interface ComponentMapper { | |||
ComponentDto selectByKey(String key); | |||
@CheckForNull | |||
ComponentDto selectByKeyAndBranch(@Param("key") String key, @Param("dbKey") String dbKey, @Param("branch") String branch); | |||
ComponentDto selectByKeyAndBranchKey(@Param("key") String key, @Param("dbKey") String dbKey, @Param("branch") String branch); | |||
@CheckForNull | |||
ComponentDto selectById(long id); |
@@ -0,0 +1,26 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.db.component; | |||
public enum KeyType { | |||
BRANCH, | |||
PULL_REQUEST | |||
} |
@@ -0,0 +1,38 @@ | |||
/* | |||
SonarQube, open source software quality management tool. | |||
Copyright (C) 2008-2016 SonarSource | |||
mailto:contact AT sonarsource DOT com | |||
SonarQube 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. | |||
SonarQube 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. | |||
*/ | |||
// Structure of db column PROJECT_BRANCHES.PULL_REQUEST_DATA | |||
syntax = "proto3"; | |||
package sonarqube.db.project_branches; | |||
// The java package can be changed without breaking compatibility. | |||
// it impacts only the generated Java code. | |||
option java_package = "org.sonar.db.protobuf"; | |||
option optimize_for = SPEED; | |||
message PullRequestData { | |||
string branch = 1; | |||
string title = 2; | |||
string url = 3; | |||
map<string, string> attributes = 4; | |||
} |
@@ -6,8 +6,10 @@ | |||
pb.uuid as uuid, | |||
pb.project_uuid as projectUuid, | |||
pb.kee as kee, | |||
pb.key_type as keyType, | |||
pb.branch_type as branchType, | |||
pb.merge_branch_uuid as mergeBranchUuid | |||
pb.merge_branch_uuid as mergeBranchUuid, | |||
pb.pull_request_binary as pullRequestBinary | |||
</sql> | |||
<insert id="insert" parameterType="map" useGeneratedKeys="false"> | |||
@@ -15,16 +17,20 @@ | |||
uuid, | |||
project_uuid, | |||
kee, | |||
key_type, | |||
branch_type, | |||
merge_branch_uuid, | |||
pull_request_binary, | |||
created_at, | |||
updated_at | |||
) values ( | |||
#{dto.uuid, jdbcType=VARCHAR}, | |||
#{dto.projectUuid, jdbcType=VARCHAR}, | |||
#{dto.kee, jdbcType=VARCHAR}, | |||
#{dto.keyType, jdbcType=VARCHAR}, | |||
#{dto.branchType, jdbcType=VARCHAR}, | |||
#{dto.mergeBranchUuid, jdbcType=VARCHAR}, | |||
#{dto.pullRequestBinary, jdbcType=BINARY}, | |||
#{now, jdbcType=BIGINT}, | |||
#{now, jdbcType=BIGINT} | |||
) | |||
@@ -43,6 +49,7 @@ | |||
update project_branches | |||
set | |||
merge_branch_uuid = #{dto.mergeBranchUuid, jdbcType=VARCHAR}, | |||
pull_request_binary = #{dto.pullRequestBinary, jdbcType=BINARY}, | |||
updated_at = #{now, jdbcType=BIGINT} | |||
where | |||
uuid = #{dto.uuid, jdbcType=VARCHAR} | |||
@@ -53,7 +60,8 @@ | |||
from project_branches pb | |||
where | |||
pb.project_uuid = #{projectUuid, jdbcType=VARCHAR} and | |||
pb.kee = #{key, jdbcType=VARCHAR} | |||
pb.kee = #{key, jdbcType=VARCHAR} and | |||
pb.key_type = #{keyType, jdbcType=VARCHAR} | |||
</select> | |||
<select id="selectByProjectUuid" parameterType="string" resultType="org.sonar.db.component.BranchDto"> |
@@ -36,7 +36,7 @@ | |||
p.kee=#{key,jdbcType=VARCHAR} | |||
</select> | |||
<select id="selectByKeyAndBranch" parameterType="String" resultType="Component"> | |||
<select id="selectByKeyAndBranchKey" parameterType="String" resultType="Component"> | |||
SELECT | |||
<include refid="componentColumns"/> | |||
FROM projects p | |||
@@ -666,7 +666,7 @@ | |||
ON p.uuid = i.component_uuid | |||
JOIN project_branches b | |||
ON i.project_uuid = b.uuid | |||
AND b.branch_type = 'SHORT' | |||
AND (b.branch_type = 'SHORT' OR b.branch_type = 'PULL_REQUEST') | |||
AND b.merge_branch_uuid = #{mergeBranchUuid,jdbcType=VARCHAR} | |||
AND i.status != 'CLOSED' | |||
</select> |
@@ -26,6 +26,7 @@ import org.sonar.api.utils.System2; | |||
import org.sonar.api.utils.internal.TestSystem2; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.protobuf.DbProjectBranches; | |||
import static java.util.Arrays.asList; | |||
import static java.util.Collections.singletonList; | |||
@@ -37,7 +38,7 @@ public class BranchDaoTest { | |||
private static final long NOW = 1_000L; | |||
private static final String SELECT_FROM = "select project_uuid as \"projectUuid\", uuid as \"uuid\", branch_type as \"branchType\", " + | |||
"kee as \"kee\", merge_branch_uuid as \"mergeBranchUuid\", created_at as \"createdAt\", updated_at as \"updatedAt\" " + | |||
"kee as \"kee\", merge_branch_uuid as \"mergeBranchUuid\", pull_request_binary as \"pullRequestBinary\", created_at as \"createdAt\", updated_at as \"updatedAt\" " + | |||
"from project_branches "; | |||
private System2 system2 = new TestSystem2().setNow(NOW); | |||
@@ -64,6 +65,7 @@ public class BranchDaoTest { | |||
entry("branchType", "SHORT"), | |||
entry("kee", "feature/foo"), | |||
entry("mergeBranchUuid", null), | |||
entry("pullRequestBinary", null), | |||
entry("createdAt", 1_000L), | |||
entry("updatedAt", 1_000L)); | |||
} | |||
@@ -85,7 +87,7 @@ public class BranchDaoTest { | |||
underTest.insert(dbSession, dto2); | |||
underTest.updateMainBranchName(dbSession, "U1", "master"); | |||
BranchDto loaded = underTest.selectByKey(dbSession, "U1", "master").get(); | |||
BranchDto loaded = underTest.selectByBranchKey(dbSession, "U1", "master").get(); | |||
assertThat(loaded.getMergeBranchUuid()).isNull(); | |||
assertThat(loaded.getProjectUuid()).isEqualTo("U1"); | |||
assertThat(loaded.getBranchType()).isEqualTo(BranchType.LONG); | |||
@@ -110,7 +112,76 @@ public class BranchDaoTest { | |||
} | |||
@Test | |||
public void upsert() { | |||
public void insert_pull_request_branch_with_only_non_null_fields() { | |||
String projectUuid = "U1"; | |||
String uuid = "U2"; | |||
BranchType branchType = BranchType.PULL_REQUEST; | |||
String kee = "123"; | |||
BranchDto dto = new BranchDto(); | |||
dto.setProjectUuid(projectUuid); | |||
dto.setUuid(uuid); | |||
dto.setBranchType(branchType); | |||
dto.setKey(kee); | |||
underTest.insert(dbSession, dto); | |||
BranchDto loaded = underTest.selectByUuid(dbSession, dto.getUuid()).get(); | |||
assertThat(loaded.getProjectUuid()).isEqualTo(projectUuid); | |||
assertThat(loaded.getUuid()).isEqualTo(uuid); | |||
assertThat(loaded.getBranchType()).isEqualTo(branchType); | |||
assertThat(loaded.getKey()).isEqualTo(kee); | |||
assertThat(loaded.getMergeBranchUuid()).isNull(); | |||
assertThat(loaded.getPullRequestData()).isNull(); | |||
} | |||
@Test | |||
public void insert_pull_request_branch_with_all_fields() { | |||
String projectUuid = "U1"; | |||
String uuid = "U2"; | |||
BranchType branchType = BranchType.PULL_REQUEST; | |||
String kee = "123"; | |||
String branch = "feature/pr1"; | |||
String title = "Dummy Feature Title"; | |||
String url = "http://example.com/pullRequests/pr1"; | |||
String tokenAttributeName = "token"; | |||
String tokenAttributeValue = "dummy token"; | |||
DbProjectBranches.PullRequestData pullRequestData = DbProjectBranches.PullRequestData.newBuilder() | |||
.setBranch(branch) | |||
.setTitle(title) | |||
.setUrl(url) | |||
.putAttributes(tokenAttributeName, tokenAttributeValue) | |||
.build(); | |||
BranchDto dto = new BranchDto(); | |||
dto.setProjectUuid(projectUuid); | |||
dto.setUuid(uuid); | |||
dto.setBranchType(branchType); | |||
dto.setKey(kee); | |||
dto.setPullRequestData(pullRequestData); | |||
underTest.insert(dbSession, dto); | |||
BranchDto loaded = underTest.selectByUuid(dbSession, dto.getUuid()).get(); | |||
assertThat(loaded.getProjectUuid()).isEqualTo(projectUuid); | |||
assertThat(loaded.getUuid()).isEqualTo(uuid); | |||
assertThat(loaded.getBranchType()).isEqualTo(branchType); | |||
assertThat(loaded.getKey()).isEqualTo(kee); | |||
assertThat(loaded.getMergeBranchUuid()).isNull(); | |||
DbProjectBranches.PullRequestData loadedPullRequestData = loaded.getPullRequestData(); | |||
assertThat(loadedPullRequestData).isNotNull(); | |||
assertThat(loadedPullRequestData.getBranch()).isEqualTo(branch); | |||
assertThat(loadedPullRequestData.getTitle()).isEqualTo(title); | |||
assertThat(loadedPullRequestData.getUrl()).isEqualTo(url); | |||
assertThat(loadedPullRequestData.getAttributesMap().get(tokenAttributeName)).isEqualTo(tokenAttributeValue); | |||
} | |||
@Test | |||
public void upsert_branch() { | |||
BranchDto dto = new BranchDto(); | |||
dto.setProjectUuid("U1"); | |||
dto.setUuid("U2"); | |||
@@ -126,14 +197,110 @@ public class BranchDaoTest { | |||
dto.setBranchType(BranchType.SHORT); | |||
underTest.upsert(dbSession, dto); | |||
BranchDto loaded = underTest.selectByKey(dbSession, "U1", "foo").get(); | |||
BranchDto loaded = underTest.selectByBranchKey(dbSession, "U1", "foo").get(); | |||
assertThat(loaded.getMergeBranchUuid()).isEqualTo("U3"); | |||
assertThat(loaded.getProjectUuid()).isEqualTo("U1"); | |||
assertThat(loaded.getBranchType()).isEqualTo(BranchType.LONG); | |||
} | |||
@Test | |||
public void selectByKey() { | |||
public void upsert_pull_request() { | |||
BranchDto dto = new BranchDto(); | |||
dto.setProjectUuid("U1"); | |||
dto.setUuid("U2"); | |||
dto.setBranchType(BranchType.PULL_REQUEST); | |||
dto.setKey("foo"); | |||
underTest.insert(dbSession, dto); | |||
// the fields that can be updated | |||
dto.setMergeBranchUuid("U3"); | |||
String branch = "feature/pr1"; | |||
String title = "Dummy Feature Title"; | |||
String url = "http://example.com/pullRequests/pr1"; | |||
String tokenAttributeName = "token"; | |||
String tokenAttributeValue = "dummy token"; | |||
DbProjectBranches.PullRequestData pullRequestData = DbProjectBranches.PullRequestData.newBuilder() | |||
.setBranch(branch) | |||
.setTitle(title) | |||
.setUrl(url) | |||
.putAttributes(tokenAttributeName, tokenAttributeValue) | |||
.build(); | |||
dto.setPullRequestData(pullRequestData); | |||
// the fields that can't be updated. New values are ignored. | |||
dto.setProjectUuid("ignored"); | |||
dto.setBranchType(BranchType.SHORT); | |||
underTest.upsert(dbSession, dto); | |||
BranchDto loaded = underTest.selectByPullRequestKey(dbSession, "U1", "foo").get(); | |||
assertThat(loaded.getMergeBranchUuid()).isEqualTo("U3"); | |||
assertThat(loaded.getProjectUuid()).isEqualTo("U1"); | |||
assertThat(loaded.getBranchType()).isEqualTo(BranchType.PULL_REQUEST); | |||
DbProjectBranches.PullRequestData loadedPullRequestData = loaded.getPullRequestData(); | |||
assertThat(loadedPullRequestData).isNotNull(); | |||
assertThat(loadedPullRequestData.getBranch()).isEqualTo(branch); | |||
assertThat(loadedPullRequestData.getTitle()).isEqualTo(title); | |||
assertThat(loadedPullRequestData.getUrl()).isEqualTo(url); | |||
assertThat(loadedPullRequestData.getAttributesMap().get(tokenAttributeName)).isEqualTo(tokenAttributeValue); | |||
} | |||
@Test | |||
public void update_pull_request_data() { | |||
BranchDto dto = new BranchDto(); | |||
dto.setProjectUuid("U1"); | |||
dto.setUuid("U2"); | |||
dto.setBranchType(BranchType.PULL_REQUEST); | |||
dto.setKey("foo"); | |||
// the fields that can be updated | |||
String mergeBranchUuid = "U3"; | |||
dto.setMergeBranchUuid(mergeBranchUuid + "-dummy-suffix"); | |||
String branch = "feature/pr1"; | |||
String title = "Dummy Feature Title"; | |||
String url = "http://example.com/pullRequests/pr1"; | |||
String tokenAttributeName = "token"; | |||
String tokenAttributeValue = "dummy token"; | |||
DbProjectBranches.PullRequestData pullRequestData = DbProjectBranches.PullRequestData.newBuilder() | |||
.setBranch(branch + "-dummy-suffix") | |||
.setTitle(title + "-dummy-suffix") | |||
.setUrl(url + "-dummy-suffix") | |||
.putAttributes(tokenAttributeName, tokenAttributeValue + "-dummy-suffix") | |||
.build(); | |||
dto.setPullRequestData(pullRequestData); | |||
underTest.insert(dbSession, dto); | |||
// modify pull request data | |||
dto.setMergeBranchUuid(mergeBranchUuid); | |||
pullRequestData = DbProjectBranches.PullRequestData.newBuilder() | |||
.setBranch(branch) | |||
.setTitle(title) | |||
.setUrl(url) | |||
.putAttributes(tokenAttributeName, tokenAttributeValue) | |||
.build(); | |||
dto.setPullRequestData(pullRequestData); | |||
underTest.upsert(dbSession, dto); | |||
BranchDto loaded = underTest.selectByPullRequestKey(dbSession, "U1", "foo").get(); | |||
assertThat(loaded.getMergeBranchUuid()).isEqualTo(mergeBranchUuid); | |||
assertThat(loaded.getProjectUuid()).isEqualTo("U1"); | |||
assertThat(loaded.getBranchType()).isEqualTo(BranchType.PULL_REQUEST); | |||
DbProjectBranches.PullRequestData loadedPullRequestData = loaded.getPullRequestData(); | |||
assertThat(loadedPullRequestData).isNotNull(); | |||
assertThat(loadedPullRequestData.getBranch()).isEqualTo(branch); | |||
assertThat(loadedPullRequestData.getTitle()).isEqualTo(title); | |||
assertThat(loadedPullRequestData.getUrl()).isEqualTo(url); | |||
assertThat(loadedPullRequestData.getAttributesMap().get(tokenAttributeName)).isEqualTo(tokenAttributeValue); | |||
} | |||
@Test | |||
public void selectByBranchKey() { | |||
BranchDto mainBranch = new BranchDto(); | |||
mainBranch.setProjectUuid("U1"); | |||
mainBranch.setUuid("U1"); | |||
@@ -150,7 +317,7 @@ public class BranchDaoTest { | |||
underTest.insert(dbSession, featureBranch); | |||
// select the feature branch | |||
BranchDto loaded = underTest.selectByKey(dbSession, "U1", "feature/foo").get(); | |||
BranchDto loaded = underTest.selectByBranchKey(dbSession, "U1", "feature/foo").get(); | |||
assertThat(loaded.getUuid()).isEqualTo(featureBranch.getUuid()); | |||
assertThat(loaded.getKey()).isEqualTo(featureBranch.getKey()); | |||
assertThat(loaded.getProjectUuid()).isEqualTo(featureBranch.getProjectUuid()); | |||
@@ -158,7 +325,37 @@ public class BranchDaoTest { | |||
assertThat(loaded.getMergeBranchUuid()).isEqualTo(featureBranch.getMergeBranchUuid()); | |||
// select a branch on another project with same branch name | |||
assertThat(underTest.selectByKey(dbSession, "U3", "feature/foo")).isEmpty(); | |||
assertThat(underTest.selectByBranchKey(dbSession, "U3", "feature/foo")).isEmpty(); | |||
} | |||
@Test | |||
public void selectByPullRequestKey() { | |||
BranchDto mainBranch = new BranchDto(); | |||
mainBranch.setProjectUuid("U1"); | |||
mainBranch.setUuid("U1"); | |||
mainBranch.setBranchType(BranchType.LONG); | |||
mainBranch.setKey("master"); | |||
underTest.insert(dbSession, mainBranch); | |||
String pullRequestId = "123"; | |||
BranchDto pullRequest = new BranchDto(); | |||
pullRequest.setProjectUuid("U1"); | |||
pullRequest.setUuid("U2"); | |||
pullRequest.setBranchType(BranchType.PULL_REQUEST); | |||
pullRequest.setKey(pullRequestId); | |||
pullRequest.setMergeBranchUuid("U3"); | |||
underTest.insert(dbSession, pullRequest); | |||
// select the feature branch | |||
BranchDto loaded = underTest.selectByPullRequestKey(dbSession, "U1", pullRequestId).get(); | |||
assertThat(loaded.getUuid()).isEqualTo(pullRequest.getUuid()); | |||
assertThat(loaded.getKey()).isEqualTo(pullRequest.getKey()); | |||
assertThat(loaded.getProjectUuid()).isEqualTo(pullRequest.getProjectUuid()); | |||
assertThat(loaded.getBranchType()).isEqualTo(pullRequest.getBranchType()); | |||
assertThat(loaded.getMergeBranchUuid()).isEqualTo(pullRequest.getMergeBranchUuid()); | |||
// select a branch on another project with same branch name | |||
assertThat(underTest.selectByPullRequestKey(dbSession, "U3", pullRequestId)).isEmpty(); | |||
} | |||
@Test |
@@ -19,12 +19,18 @@ | |||
*/ | |||
package org.sonar.db.component; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.db.protobuf.DbProjectBranches; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class BranchDtoTest { | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
private BranchDto underTest = new BranchDto(); | |||
@Test | |||
@@ -42,4 +48,30 @@ public class BranchDtoTest { | |||
assertThat(underTest.isMain()).isFalse(); | |||
} | |||
@Test | |||
public void encode_and_decode_pull_request_data() { | |||
String branch = "feature/pr1"; | |||
String title = "Dummy Feature Title"; | |||
String url = "http://example.com/pullRequests/pr1"; | |||
DbProjectBranches.PullRequestData pullRequestData = DbProjectBranches.PullRequestData.newBuilder() | |||
.setBranch(branch) | |||
.setTitle(title) | |||
.setUrl(url) | |||
.build(); | |||
underTest.setPullRequestData(pullRequestData); | |||
DbProjectBranches.PullRequestData decoded = underTest.getPullRequestData(); | |||
assertThat(decoded).isNotNull(); | |||
assertThat(decoded.getBranch()).isEqualTo(branch); | |||
assertThat(decoded.getTitle()).isEqualTo(title); | |||
assertThat(decoded.getUrl()).isEqualTo(url); | |||
} | |||
@Test | |||
public void getPullRequestData_returns_null_when_data_is_null() { | |||
assertThat(underTest.getPullRequestData()).isNull(); | |||
} | |||
} |
@@ -128,4 +128,15 @@ public class ComponentDtoTest { | |||
assertThat(underTest.getKey()).isEqualTo("my_key"); | |||
assertThat(underTest.getBranch()).isNull(); | |||
} | |||
@Test | |||
public void getKey_and_getPullRequest() { | |||
ComponentDto underTest = new ComponentDto().setDbKey("my_key:PULL_REQUEST:pr-123"); | |||
assertThat(underTest.getKey()).isEqualTo("my_key"); | |||
assertThat(underTest.getPullRequest()).isEqualTo("pr-123"); | |||
underTest = new ComponentDto().setDbKey("my_key"); | |||
assertThat(underTest.getKey()).isEqualTo("my_key"); | |||
assertThat(underTest.getPullRequest()).isNull(); | |||
} | |||
} |
@@ -34,6 +34,7 @@ import org.sonar.db.organization.OrganizationDto; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.api.Assertions.entry; | |||
import static org.sonar.db.component.BranchType.PULL_REQUEST; | |||
import static org.sonar.db.component.ComponentKeyUpdaterDao.computeNewKey; | |||
import static org.sonar.db.component.ComponentTesting.newDirectory; | |||
import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
@@ -105,6 +106,33 @@ public class ComponentKeyUpdaterDaoTest { | |||
.forEach(map -> map.values().forEach(k -> assertThat(k.toString()).startsWith(newProjectKey))); | |||
} | |||
@Test | |||
public void updateKey_updates_pull_requests_too() { | |||
ComponentDto project = db.components().insertMainBranch(); | |||
ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST)); | |||
db.components().insertComponent(newFileDto(pullRequest)); | |||
db.components().insertComponent(newFileDto(pullRequest)); | |||
int branchComponentCount = 3; | |||
String oldProjectKey = project.getKey(); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).hasSize(1); | |||
String oldBranchKey = pullRequest.getDbKey(); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldBranchKey)).hasSize(branchComponentCount); | |||
String newProjectKey = "newKey"; | |||
String newBranchKey = ComponentDto.generatePullRequestKey(newProjectKey, pullRequest.getPullRequest()); | |||
underTest.updateKey(dbSession, project.uuid(), newProjectKey); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).isEmpty(); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldBranchKey)).isEmpty(); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newProjectKey)).hasSize(1); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newBranchKey)).hasSize(branchComponentCount); | |||
db.select(dbSession, "select kee from projects") | |||
.forEach(map -> map.values().forEach(k -> assertThat(k.toString()).startsWith(newProjectKey))); | |||
} | |||
@Test | |||
public void bulk_updateKey_updates_branches_too() { | |||
ComponentDto project = db.components().insertMainBranch(); | |||
@@ -133,6 +161,34 @@ public class ComponentKeyUpdaterDaoTest { | |||
.forEach(map -> map.values().forEach(k -> assertThat(k.toString()).startsWith(newProjectKey))); | |||
} | |||
@Test | |||
public void bulk_updateKey_updates_pull_requests_too() { | |||
ComponentDto project = db.components().insertMainBranch(); | |||
ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST)); | |||
ComponentDto module = db.components().insertComponent(prefixDbKeyWithKey(newModuleDto(pullRequest), project.getKey())); | |||
db.components().insertComponent(prefixDbKeyWithKey(newFileDto(module), module.getKey())); | |||
db.components().insertComponent(prefixDbKeyWithKey(newFileDto(module), module.getKey())); | |||
int branchComponentCount = 4; | |||
String oldProjectKey = project.getKey(); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).hasSize(1); | |||
String oldPullRequestKey = pullRequest.getDbKey(); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldPullRequestKey)).hasSize(branchComponentCount); | |||
String newProjectKey = "newKey"; | |||
String newPullRequestKey = ComponentDto.generatePullRequestKey(newProjectKey, pullRequest.getPullRequest()); | |||
underTest.bulkUpdateKey(dbSession, project.uuid(), oldProjectKey, newProjectKey); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).isEmpty(); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldPullRequestKey)).isEmpty(); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newProjectKey)).hasSize(1); | |||
assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newPullRequestKey)).hasSize(branchComponentCount); | |||
db.select(dbSession, "select kee from projects") | |||
.forEach(map -> map.values().forEach(k -> assertThat(k.toString()).startsWith(newProjectKey))); | |||
} | |||
private ComponentDto prefixDbKeyWithKey(ComponentDto componentDto, String key) { | |||
return componentDto.setDbKey(key + ":" + componentDto.getDbKey()); | |||
} |
@@ -29,6 +29,7 @@ import org.sonar.db.organization.OrganizationDto; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static com.google.common.base.Preconditions.checkNotNull; | |||
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; | |||
import static org.sonar.db.component.BranchType.PULL_REQUEST; | |||
import static org.sonar.db.component.ComponentDto.UUID_PATH_SEPARATOR; | |||
public class ComponentTesting { | |||
@@ -99,7 +100,15 @@ public class ComponentTesting { | |||
private static String generateKey(String key, ComponentDto parentModuleOrProject) { | |||
String branch = parentModuleOrProject.getBranch(); | |||
return branch == null ? key : ComponentDto.generateBranchKey(key, branch); | |||
if (branch != null) { | |||
return ComponentDto.generateBranchKey(key, branch); | |||
} | |||
String pullRequest = parentModuleOrProject.getPullRequest(); | |||
if (pullRequest != null) { | |||
return ComponentDto.generatePullRequestKey(key, pullRequest); | |||
} | |||
return key; | |||
} | |||
public static ComponentDto newModuleDto(ComponentDto subProjectOrProject) { | |||
@@ -231,6 +240,7 @@ public class ComponentTesting { | |||
checkArgument(project.qualifier().equals(Qualifiers.PROJECT)); | |||
checkArgument(project.getMainBranchProjectUuid() == null); | |||
String branchName = branchDto.getKey(); | |||
String branchSeparator = branchDto.getBranchType() == PULL_REQUEST ? ":PULL_REQUEST:" : ":BRANCH:"; | |||
String uuid = branchDto.getUuid(); | |||
return new ComponentDto() | |||
.setUuid(uuid) | |||
@@ -240,7 +250,7 @@ public class ComponentTesting { | |||
.setModuleUuidPath(UUID_PATH_SEPARATOR + uuid + UUID_PATH_SEPARATOR) | |||
.setRootUuid(uuid) | |||
// name of the branch is not mandatory on the main branch | |||
.setDbKey(branchName != null ? project.getDbKey() + ":BRANCH:" + branchName : project.getKey()) | |||
.setDbKey(branchName != null ? project.getDbKey() + branchSeparator + branchName : project.getKey()) | |||
.setMainBranchProjectUuid(project.uuid()) | |||
.setName(project.name()) | |||
.setLongName(project.longName()) |
@@ -0,0 +1,46 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.def.VarcharColumnDef; | |||
import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder; | |||
import org.sonar.server.platform.db.migration.step.DdlChange; | |||
public class AddKeyTypeInProjectBranches extends DdlChange { | |||
public static final String TABLE_NAME = "project_branches"; | |||
public AddKeyTypeInProjectBranches(Database db) { | |||
super(db); | |||
} | |||
@Override | |||
public void execute(Context context) throws SQLException { | |||
context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME) | |||
.addColumn(VarcharColumnDef.newVarcharColumnDefBuilder() | |||
.setColumnName("key_type") | |||
.setIsNullable(true) | |||
.setLimit(12) | |||
.build()) | |||
.build()); | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.def.BlobColumnDef; | |||
import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder; | |||
import org.sonar.server.platform.db.migration.step.DdlChange; | |||
public class AddPullRequestBinaryInProjectBranches extends DdlChange { | |||
static final String TABLE_NAME = "project_branches"; | |||
static final String COLUMN_NAME = "pull_request_binary"; | |||
public AddPullRequestBinaryInProjectBranches(Database db) { | |||
super(db); | |||
} | |||
@Override | |||
public void execute(Context context) throws SQLException { | |||
context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME) | |||
.addColumn(BlobColumnDef.newBlobColumnDefBuilder() | |||
.setColumnName(COLUMN_NAME) | |||
.setIsNullable(true) | |||
.build()) | |||
.build()); | |||
} | |||
} |
@@ -43,6 +43,12 @@ public class DbVersion71 implements DbVersion { | |||
.add(2013, "Create WEBHOOKS Table", CreateWebhooksTable.class) | |||
.add(2014, "Migrate webhooks from SETTINGS table to WEBHOOKS table", MigrateWebhooksToWebhooksTable.class) | |||
.add(2015, "Add webhook key to WEBHOOK_DELIVERIES table", AddWebhookKeyToWebhookDeliveriesTable.class) | |||
.add(2016, "Increase branch type size in PROJECT_BRANCHES", IncreaseBranchTypeSizeForPullRequest.class) | |||
.add(2017, "Add key_type column in PROJECT_BRANCHES", AddKeyTypeInProjectBranches.class) | |||
.add(2018, "Fill key_type column in PROJECT_BRANCHES", SetKeyTypeToBranchInProjectBranches.class) | |||
.add(2019, "Make key_type not nullable in PROJECT_BRANCHES", MakeKeyTypeNotNullableInProjectBranches.class) | |||
.add(2020, "Replace index in PROJECT_BRANCHES", ReplaceIndexInProjectBranches.class) | |||
.add(2021, "Add pull_request_data in PROJECT_BRANCHES", AddPullRequestBinaryInProjectBranches.class) | |||
; | |||
} | |||
} |
@@ -0,0 +1,45 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder; | |||
import org.sonar.server.platform.db.migration.step.DdlChange; | |||
import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder; | |||
public class IncreaseBranchTypeSizeForPullRequest extends DdlChange { | |||
private static final String TABLE_NAME = "project_branches"; | |||
public IncreaseBranchTypeSizeForPullRequest(Database db) { | |||
super(db); | |||
} | |||
@Override | |||
public void execute(Context context) throws SQLException { | |||
context.execute(new AlterColumnsBuilder(getDialect(), TABLE_NAME) | |||
.updateColumn(newVarcharColumnDefBuilder() | |||
.setColumnName("branch_type") | |||
.setLimit(12) | |||
.build()) | |||
.build()); | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder; | |||
import org.sonar.server.platform.db.migration.step.DdlChange; | |||
import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder; | |||
public class MakeKeyTypeNotNullableInProjectBranches extends DdlChange { | |||
static final String TABLE_NAME = "project_branches"; | |||
public MakeKeyTypeNotNullableInProjectBranches(Database db) { | |||
super(db); | |||
} | |||
@Override | |||
public void execute(Context context) throws SQLException { | |||
context.execute(new AlterColumnsBuilder(getDialect(), TABLE_NAME) | |||
.updateColumn(newVarcharColumnDefBuilder() | |||
.setColumnName("key_type") | |||
.setLimit(12) | |||
.setIsNullable(false) | |||
.build()) | |||
.build()); | |||
} | |||
} |
@@ -0,0 +1,74 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.def.VarcharColumnDef; | |||
import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder; | |||
import org.sonar.server.platform.db.migration.sql.DropIndexBuilder; | |||
import org.sonar.server.platform.db.migration.step.DdlChange; | |||
public class ReplaceIndexInProjectBranches extends DdlChange { | |||
static final String TABLE_NAME = "project_branches"; | |||
private static final String OLD_INDEX_NAME = "project_branches_kee"; | |||
static final String NEW_INDEX_NAME = "project_branches_kee_key_type"; | |||
static final VarcharColumnDef PROJECT_UUID_COLUMN = VarcharColumnDef.newVarcharColumnDefBuilder() | |||
.setColumnName("project_uuid") | |||
.setIsNullable(false) | |||
.setLimit(50) | |||
.build(); | |||
static final VarcharColumnDef KEE_COLUMN = VarcharColumnDef.newVarcharColumnDefBuilder() | |||
.setColumnName("kee") | |||
.setIsNullable(false) | |||
.setLimit(255) | |||
.build(); | |||
static final VarcharColumnDef KEY_TYPE_COLUMN = VarcharColumnDef.newVarcharColumnDefBuilder() | |||
.setColumnName("key_type") | |||
.setIsNullable(false) | |||
.setLimit(12) | |||
.build(); | |||
public ReplaceIndexInProjectBranches(Database db) { | |||
super(db); | |||
} | |||
@Override | |||
public void execute(Context context) throws SQLException { | |||
context.execute(new DropIndexBuilder(getDialect()) | |||
.setTable(TABLE_NAME) | |||
.setName(OLD_INDEX_NAME) | |||
.build()); | |||
context.execute(new CreateIndexBuilder(getDialect()) | |||
.addColumn(PROJECT_UUID_COLUMN) | |||
.addColumn(KEE_COLUMN) | |||
.addColumn(KEY_TYPE_COLUMN) | |||
.setUnique(true) | |||
.setTable(TABLE_NAME) | |||
.setName(NEW_INDEX_NAME) | |||
.build() | |||
); | |||
} | |||
} |
@@ -0,0 +1,53 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.step.DataChange; | |||
import org.sonar.server.platform.db.migration.step.MassUpdate; | |||
public class SetKeyTypeToBranchInProjectBranches extends DataChange { | |||
static final String TABLE_NAME = "project_branches"; | |||
static final String DEFAULT_KEY_TYPE = "BRANCH"; | |||
private final System2 system2; | |||
public SetKeyTypeToBranchInProjectBranches(Database db, System2 system2) { | |||
super(db); | |||
this.system2 = system2; | |||
} | |||
@Override | |||
protected void execute(Context context) throws SQLException { | |||
long now = system2.now(); | |||
MassUpdate massUpdate = context.prepareMassUpdate(); | |||
massUpdate.rowPluralName("branches"); | |||
massUpdate.select("select uuid from " + TABLE_NAME + " where key_type is null"); | |||
massUpdate.update("update " + TABLE_NAME + " set key_type=?, updated_at=? where uuid = ?"); | |||
massUpdate.execute((row, update) -> { | |||
update.setString(1, DEFAULT_KEY_TYPE); | |||
update.setLong(2, now); | |||
update.setString(3, row.getString(1)); | |||
return true; | |||
}); | |||
} | |||
} |
@@ -0,0 +1,56 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.db.CoreDbTester; | |||
import static java.sql.Types.VARCHAR; | |||
public class AddKeyTypeInProjectBranchesTest { | |||
public static final String TABLE_NAME = "project_branches"; | |||
@Rule | |||
public final CoreDbTester dbTester = CoreDbTester.createForSchema(AddKeyTypeInProjectBranchesTest.class, TABLE_NAME + ".sql"); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
private AddKeyTypeInProjectBranches underTest = new AddKeyTypeInProjectBranches(dbTester.database()); | |||
@Test | |||
public void column_is_added_to_table() throws SQLException { | |||
underTest.execute(); | |||
dbTester.assertColumnDefinition(TABLE_NAME, "key_type", VARCHAR, null, true); | |||
} | |||
@Test | |||
public void migration_is_not_reentrant() throws SQLException { | |||
underTest.execute(); | |||
expectedException.expect(IllegalStateException.class); | |||
underTest.execute(); | |||
} | |||
} |
@@ -0,0 +1,57 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.db.CoreDbTester; | |||
import static java.sql.Types.BLOB; | |||
import static org.sonar.server.platform.db.migration.version.v71.AddPullRequestBinaryInProjectBranches.COLUMN_NAME; | |||
import static org.sonar.server.platform.db.migration.version.v71.AddPullRequestBinaryInProjectBranches.TABLE_NAME; | |||
public class AddPullRequestBinaryInProjectBranchesTest { | |||
@Rule | |||
public final CoreDbTester dbTester = CoreDbTester.createForSchema(AddPullRequestBinaryInProjectBranchesTest.class, TABLE_NAME + ".sql"); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
private AddPullRequestBinaryInProjectBranches underTest = new AddPullRequestBinaryInProjectBranches(dbTester.database()); | |||
@Test | |||
public void column_is_added_to_table() throws SQLException { | |||
underTest.execute(); | |||
dbTester.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, BLOB, null, true); | |||
} | |||
@Test | |||
public void migration_is_not_reentrant() throws SQLException { | |||
underTest.execute(); | |||
expectedException.expect(IllegalStateException.class); | |||
underTest.execute(); | |||
} | |||
} |
@@ -36,7 +36,7 @@ public class DbVersion71Test { | |||
@Test | |||
public void verify_migration_count() { | |||
verifyMigrationCount(underTest, 16); | |||
verifyMigrationCount(underTest, 22); | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import java.sql.Types; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.db.CoreDbTester; | |||
import org.sonar.scanner.protocol.output.ScannerReport; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class IncreaseBranchTypeSizeForPullRequestTest { | |||
private static final String TABLE_NAME = "project_branches"; | |||
@Rule | |||
public CoreDbTester db = CoreDbTester.createForSchema(IncreaseBranchTypeSizeForPullRequestTest.class, "project_branches.sql"); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
private IncreaseBranchTypeSizeForPullRequest underTest = new IncreaseBranchTypeSizeForPullRequest(db.database()); | |||
@Test | |||
public void cannot_insert_PULL_REQUEST_type_before_migration() { | |||
expectedException.expect(IllegalStateException.class); | |||
insertRow(); | |||
} | |||
@Test | |||
public void can_insert_PULL_REQUEST_after_execute() throws SQLException { | |||
underTest.execute(); | |||
assertThat(db.countRowsOfTable(TABLE_NAME)).isEqualTo(0); | |||
insertRow(); | |||
assertThat(db.countRowsOfTable(TABLE_NAME)).isEqualTo(1); | |||
} | |||
private void insertRow() { | |||
db.executeInsert( | |||
"PROJECT_BRANCHES", | |||
"UUID", "dummy_uuid", | |||
"PROJECT_UUID", "dummy_project_uuid", | |||
"KEE", "dummy_key", | |||
"CREATED_AT", 456789, | |||
"UPDATED_AT", 456789, | |||
"BRANCH_TYPE", "PULL_REQUEST"); | |||
} | |||
} |
@@ -0,0 +1,61 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import java.sql.Types; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.rule.RuleScope; | |||
import org.sonar.db.CoreDbTester; | |||
import static org.sonar.server.platform.db.migration.version.v71.MakeKeyTypeNotNullableInProjectBranches.TABLE_NAME; | |||
public class MakeKeyTypeNotNullableInProjectBranchesTest { | |||
@Rule | |||
public CoreDbTester db = CoreDbTester.createForSchema(MakeKeyTypeNotNullableInProjectBranchesTest.class, "project_branches.sql"); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
private MakeKeyTypeNotNullableInProjectBranches underTest = new MakeKeyTypeNotNullableInProjectBranches(db.database()); | |||
@Test | |||
public void execute_makes_column_not_null() throws SQLException { | |||
db.assertColumnDefinition(TABLE_NAME, "key_type", Types.VARCHAR, null, true); | |||
insertRow(); | |||
underTest.execute(); | |||
db.assertColumnDefinition(TABLE_NAME, "key_type", Types.VARCHAR, null, false); | |||
} | |||
private void insertRow() { | |||
db.executeInsert( | |||
"PROJECT_BRANCHES", | |||
"UUID", "dummy_uuid", | |||
"PROJECT_UUID", "dummy_project_uuid", | |||
"KEE", "dummy_key", | |||
"KEY_TYPE", "BRANCH", | |||
"CREATED_AT", 456789, | |||
"UPDATED_AT", 456789, | |||
"BRANCH_TYPE", "BRANCH"); | |||
} | |||
} |
@@ -0,0 +1,87 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.db.CoreDbTester; | |||
import static org.sonar.server.platform.db.migration.version.v71.ReplaceIndexInProjectBranches.NEW_INDEX_NAME; | |||
import static org.sonar.server.platform.db.migration.version.v71.ReplaceIndexInProjectBranches.KEE_COLUMN; | |||
import static org.sonar.server.platform.db.migration.version.v71.ReplaceIndexInProjectBranches.KEY_TYPE_COLUMN; | |||
import static org.sonar.server.platform.db.migration.version.v71.ReplaceIndexInProjectBranches.PROJECT_UUID_COLUMN; | |||
import static org.sonar.server.platform.db.migration.version.v71.ReplaceIndexInProjectBranches.TABLE_NAME; | |||
public class ReplaceIndexInProjectBranchesTest { | |||
@Rule | |||
public final CoreDbTester dbTester = CoreDbTester.createForSchema(ReplaceIndexInProjectBranchesTest.class, "project_branches.sql"); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
private ReplaceIndexInProjectBranches underTest = new ReplaceIndexInProjectBranches(dbTester.database()); | |||
@Test | |||
public void column_is_part_of_index() throws SQLException { | |||
underTest.execute(); | |||
dbTester.assertUniqueIndex(TABLE_NAME, NEW_INDEX_NAME, PROJECT_UUID_COLUMN.getName(), KEE_COLUMN.getName(), KEY_TYPE_COLUMN.getName()); | |||
} | |||
@Test | |||
public void adding_pr_with_same_key_as_existing_branch_fails_before_migration() { | |||
expectedException.expect(IllegalStateException.class); | |||
String key = "feature/foo"; | |||
insertBranch(1, key); | |||
insertPullRequest(2, key); | |||
} | |||
@Test | |||
public void adding_pr_with_same_key_as_existing_branch_works_after_migration() throws SQLException { | |||
underTest.execute(); | |||
String key = "feature/foo"; | |||
insertBranch(1, key); | |||
insertPullRequest(2, key); | |||
} | |||
private void insertBranch(int id, String name) { | |||
insertRow(id, "SHORT", name, "BRANCH"); | |||
} | |||
private void insertPullRequest(int id, String pullRequestId) { | |||
insertRow(id, "PULL_REQUEST", pullRequestId, "PULL_REQUEST"); | |||
} | |||
private void insertRow(int id, String branchType, String key, String keyType) { | |||
dbTester.executeInsert( | |||
"PROJECT_BRANCHES", | |||
"UUID", "dummy_uuid" + id, | |||
"PROJECT_UUID", "dummy_project_uuid", | |||
"KEE", key, | |||
"KEY_TYPE", keyType, | |||
"CREATED_AT", 456789 + id, | |||
"UPDATED_AT", 456789 + id, | |||
"BRANCH_TYPE", branchType); | |||
} | |||
} |
@@ -0,0 +1,108 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.platform.db.migration.version.v71; | |||
import java.sql.SQLException; | |||
import javax.annotation.Nullable; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.api.utils.internal.TestSystem2; | |||
import org.sonar.db.CoreDbTester; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.sonar.server.platform.db.migration.version.v71.SetKeyTypeToBranchInProjectBranches.DEFAULT_KEY_TYPE; | |||
import static org.sonar.server.platform.db.migration.version.v71.SetKeyTypeToBranchInProjectBranches.TABLE_NAME; | |||
public class SetKeyTypeToBranchInProjectBranchesTest { | |||
private static final long PAST = 10_000_000_000L; | |||
private static final long NOW = 50_000_000_000L; | |||
private System2 system2 = new TestSystem2().setNow(NOW); | |||
@Rule | |||
public final CoreDbTester dbTester = CoreDbTester.createForSchema(SetKeyTypeToBranchInProjectBranchesTest.class, "project_branches.sql"); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
private SetKeyTypeToBranchInProjectBranches underTest = new SetKeyTypeToBranchInProjectBranches(dbTester.database(), system2); | |||
@Test | |||
public void has_no_effect_if_table_project_branches_is_empty() throws SQLException { | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable(TABLE_NAME)).isEqualTo(0); | |||
} | |||
@Test | |||
public void updates_rows_to_BRANCH() throws SQLException { | |||
insertRow(1, "SHORT"); | |||
insertRow(2, "LONG"); | |||
insertRow(3, "SHORT"); | |||
insertRow(4, "LONG"); | |||
String countUpdatedAtSQL = "select count(uuid) from " + TABLE_NAME + " where updated_at = "; | |||
assertThat(countRowsWithValue(null)).isEqualTo(4); | |||
assertThat(countRowsWithValue(DEFAULT_KEY_TYPE)).isEqualTo(0); | |||
assertThat(dbTester.countSql(countUpdatedAtSQL + PAST)).isEqualTo(4); | |||
underTest.execute(); | |||
assertThat(countRowsWithValue(null)).isEqualTo(0); | |||
assertThat(countRowsWithValue(DEFAULT_KEY_TYPE)).isEqualTo(4); | |||
assertThat(dbTester.countSql(countUpdatedAtSQL + NOW)).isEqualTo(4); | |||
} | |||
@Test | |||
public void execute_is_reentreant() throws SQLException { | |||
insertRow(1, "SHORT"); | |||
insertRow(2, "LONG"); | |||
insertRow(3, "SHORT"); | |||
insertRow(4, "LONG"); | |||
underTest.execute(); | |||
underTest.execute(); | |||
assertThat(countRowsWithValue(null)).isEqualTo(0); | |||
assertThat(countRowsWithValue(DEFAULT_KEY_TYPE)).isEqualTo(4); | |||
} | |||
private int countRowsWithValue(@Nullable String value) { | |||
if (value == null) { | |||
return dbTester.countSql("select count(1) from " + TABLE_NAME + " where key_type is null"); | |||
} | |||
return dbTester.countSql("select count(1) from " + TABLE_NAME + " where key_type = '" + value + "'"); | |||
} | |||
private void insertRow(int id, String branchType) { | |||
dbTester.executeInsert( | |||
"PROJECT_BRANCHES", | |||
"UUID", "dummy_uuid" + id, | |||
"PROJECT_UUID", "dummy_project_uuid" + id, | |||
"KEE", "dummy_key" + id, | |||
"CREATED_AT", PAST, | |||
"UPDATED_AT", PAST, | |||
"BRANCH_TYPE", branchType); | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
CREATE TABLE "PROJECT_BRANCHES" ( | |||
"UUID" VARCHAR(50) NOT NULL PRIMARY KEY, | |||
"PROJECT_UUID" VARCHAR(50) NOT NULL, | |||
"KEE" VARCHAR(255) NOT NULL, | |||
"BRANCH_TYPE" VARCHAR(12), | |||
"MERGE_BRANCH_UUID" VARCHAR(50), | |||
"CREATED_AT" BIGINT NOT NULL, | |||
"UPDATED_AT" BIGINT NOT NULL | |||
); | |||
CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID"); | |||
CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE"); |
@@ -0,0 +1,12 @@ | |||
CREATE TABLE "PROJECT_BRANCHES" ( | |||
"UUID" VARCHAR(50) NOT NULL PRIMARY KEY, | |||
"PROJECT_UUID" VARCHAR(50) NOT NULL, | |||
"KEE" VARCHAR(255) NOT NULL, | |||
"KEY_TYPE" VARCHAR(12) NOT NULL, | |||
"BRANCH_TYPE" VARCHAR(12), | |||
"MERGE_BRANCH_UUID" VARCHAR(50), | |||
"CREATED_AT" BIGINT NOT NULL, | |||
"UPDATED_AT" BIGINT NOT NULL | |||
); | |||
CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID"); | |||
CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE", "KEY_TYPE"); |
@@ -0,0 +1,11 @@ | |||
CREATE TABLE "PROJECT_BRANCHES" ( | |||
"UUID" VARCHAR(50) NOT NULL PRIMARY KEY, | |||
"PROJECT_UUID" VARCHAR(50) NOT NULL, | |||
"KEE" VARCHAR(255) NOT NULL, | |||
"BRANCH_TYPE" VARCHAR(5), | |||
"MERGE_BRANCH_UUID" VARCHAR(50), | |||
"CREATED_AT" BIGINT NOT NULL, | |||
"UPDATED_AT" BIGINT NOT NULL | |||
); | |||
CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID"); | |||
CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE"); |
@@ -0,0 +1,12 @@ | |||
CREATE TABLE "PROJECT_BRANCHES" ( | |||
"UUID" VARCHAR(50) NOT NULL PRIMARY KEY, | |||
"PROJECT_UUID" VARCHAR(50) NOT NULL, | |||
"KEE" VARCHAR(255) NOT NULL, | |||
"KEY_TYPE" VARCHAR(12) NULL, | |||
"BRANCH_TYPE" VARCHAR(12), | |||
"MERGE_BRANCH_UUID" VARCHAR(50), | |||
"CREATED_AT" BIGINT NOT NULL, | |||
"UPDATED_AT" BIGINT NOT NULL | |||
); | |||
CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID"); | |||
CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE"); |
@@ -0,0 +1,12 @@ | |||
CREATE TABLE "PROJECT_BRANCHES" ( | |||
"UUID" VARCHAR(50) NOT NULL PRIMARY KEY, | |||
"PROJECT_UUID" VARCHAR(50) NOT NULL, | |||
"KEE" VARCHAR(255) NOT NULL, | |||
"KEY_TYPE" VARCHAR(12) NOT NULL, | |||
"BRANCH_TYPE" VARCHAR(12), | |||
"MERGE_BRANCH_UUID" VARCHAR(50), | |||
"CREATED_AT" BIGINT NOT NULL, | |||
"UPDATED_AT" BIGINT NOT NULL | |||
); | |||
CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID"); | |||
CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE"); |
@@ -0,0 +1,12 @@ | |||
CREATE TABLE "PROJECT_BRANCHES" ( | |||
"UUID" VARCHAR(50) NOT NULL PRIMARY KEY, | |||
"PROJECT_UUID" VARCHAR(50) NOT NULL, | |||
"KEE" VARCHAR(255) NOT NULL, | |||
"KEY_TYPE" VARCHAR(12) NULL, | |||
"BRANCH_TYPE" VARCHAR(12), | |||
"MERGE_BRANCH_UUID" VARCHAR(50), | |||
"CREATED_AT" BIGINT NOT NULL, | |||
"UPDATED_AT" BIGINT NOT NULL | |||
); | |||
CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID"); | |||
CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE"); |
@@ -24,10 +24,12 @@ import org.sonar.api.config.Configuration; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.api.config.internal.ConfigurationBridge; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.component.BranchType; | |||
import org.sonar.server.computation.task.projectanalysis.analysis.Branch; | |||
import org.sonar.server.settings.ChildSettings; | |||
import static org.sonar.db.component.ComponentDto.generateBranchKey; | |||
import static org.sonar.db.component.ComponentDto.generatePullRequestKey; | |||
@ComputeEngineSide | |||
public class ProjectConfigurationFactory { | |||
@@ -43,7 +45,11 @@ public class ProjectConfigurationFactory { | |||
public Configuration newProjectConfiguration(String projectKey, Branch branch) { | |||
Settings projectSettings = new ChildSettings(globalSettings); | |||
addSettings(projectSettings, projectKey); | |||
addSettings(projectSettings, generateBranchKey(projectKey, branch.getName())); | |||
if (branch.getType() == BranchType.PULL_REQUEST) { | |||
addSettings(projectSettings, generatePullRequestKey(projectKey, branch.getPullRequestId())); | |||
} else { | |||
addSettings(projectSettings, generateBranchKey(projectKey, branch.getName())); | |||
} | |||
return new ConfigurationBridge(projectSettings); | |||
} | |||
@@ -55,7 +55,6 @@ import static org.sonar.api.measures.CoreMetrics.SECURITY_RATING_KEY; | |||
import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY; | |||
import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY; | |||
import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY; | |||
import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY; | |||
import static org.sonar.api.measures.Metric.Level; | |||
import static org.sonar.api.measures.Metric.ValueType; | |||
import static org.sonar.api.measures.Metric.Level.ERROR; | |||
@@ -73,12 +72,14 @@ import static org.sonar.server.computation.task.projectanalysis.qualitymodel.Rat | |||
import static org.sonar.server.computation.task.projectanalysis.qualitymodel.Rating.valueOf; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
import static org.sonarqube.ws.MediaTypes.SVG; | |||
public class MeasureAction implements ProjectBadgesWsAction { | |||
private static final String PARAM_PROJECT = "project"; | |||
private static final String PARAM_BRANCH = "branch"; | |||
private static final String PARAM_PULL_REQUEST = "pullRequest"; | |||
private static final String PARAM_METRIC = "metric"; | |||
private static final Map<String, String> METRIC_NAME_BY_KEY = ImmutableMap.<String, String>builder() | |||
@@ -140,6 +141,10 @@ public class MeasureAction implements ProjectBadgesWsAction { | |||
.createParam(PARAM_BRANCH) | |||
.setDescription("Branch key") | |||
.setExampleValue(KEY_BRANCH_EXAMPLE_001); | |||
action | |||
.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001); | |||
action.createParam(PARAM_METRIC) | |||
.setDescription("Metric key") | |||
.setRequired(true) | |||
@@ -151,9 +156,10 @@ public class MeasureAction implements ProjectBadgesWsAction { | |||
response.stream().setMediaType(SVG); | |||
String projectKey = request.mandatoryParam(PARAM_PROJECT); | |||
String branch = request.param(PARAM_BRANCH); | |||
String pullRequest = request.param(PARAM_PULL_REQUEST); | |||
String metricKey = request.mandatoryParam(PARAM_METRIC); | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
ComponentDto project = componentFinder.getByKeyAndOptionalBranch(dbSession, projectKey, branch); | |||
ComponentDto project = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branch, pullRequest); | |||
userSession.checkComponentPermission(USER, project); | |||
MetricDto metric = dbClient.metricDao().selectByKey(dbSession, metricKey); | |||
checkState(metric != null && metric.isEnabled(), "Metric '%s' hasn't been found", metricKey); |
@@ -41,12 +41,14 @@ import static org.apache.commons.io.IOUtils.write; | |||
import static org.sonar.api.web.UserRole.USER; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
import static org.sonarqube.ws.MediaTypes.SVG; | |||
public class QualityGateAction implements ProjectBadgesWsAction { | |||
private static final String PARAM_PROJECT = "project"; | |||
private static final String PARAM_BRANCH = "branch"; | |||
private static final String PARAM_PULL_REQUEST = "pullRequest"; | |||
private final UserSession userSession; | |||
private final DbClient dbClient; | |||
@@ -76,6 +78,10 @@ public class QualityGateAction implements ProjectBadgesWsAction { | |||
.createParam(PARAM_BRANCH) | |||
.setDescription("Branch key") | |||
.setExampleValue(KEY_BRANCH_EXAMPLE_001); | |||
action | |||
.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001); | |||
} | |||
@Override | |||
@@ -83,8 +89,9 @@ public class QualityGateAction implements ProjectBadgesWsAction { | |||
response.stream().setMediaType(SVG); | |||
String projectKey = request.mandatoryParam(PARAM_PROJECT); | |||
String branch = request.param(PARAM_BRANCH); | |||
String pullRequest = request.param(PARAM_PULL_REQUEST); | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
ComponentDto project = componentFinder.getByKeyAndOptionalBranch(dbSession, projectKey, branch); | |||
ComponentDto project = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branch, pullRequest); | |||
userSession.checkComponentPermission(USER, project); | |||
Level qualityGateStatus = getQualityGate(dbSession, project); | |||
write(svgGenerator.generateQualityGate(qualityGateStatus), response.stream().output(), UTF_8); |
@@ -33,6 +33,7 @@ import org.sonarqube.ws.Batch.WsProjectResponse.FileData.Builder; | |||
import static org.sonar.core.util.Protobuf.setNullable; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
public class ProjectAction implements BatchWsAction { | |||
@@ -41,6 +42,7 @@ public class ProjectAction implements BatchWsAction { | |||
private static final String PARAM_PROFILE = "profile"; | |||
private static final String PARAM_ISSUES_MODE = "issues_mode"; | |||
private static final String PARAM_BRANCH = "branch"; | |||
private static final String PARAM_PULL_REQUEST = "pullRequest"; | |||
private final ProjectDataLoader projectDataLoader; | |||
@@ -79,6 +81,12 @@ public class ProjectAction implements BatchWsAction { | |||
.setSince("6.6") | |||
.setDescription("Branch key") | |||
.setExampleValue(KEY_BRANCH_EXAMPLE_001); | |||
action | |||
.createParam(PARAM_PULL_REQUEST) | |||
.setSince("7.1") | |||
.setDescription("Pull request id") | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001); | |||
} | |||
@Override | |||
@@ -87,7 +95,8 @@ public class ProjectAction implements BatchWsAction { | |||
.setModuleKey(wsRequest.mandatoryParam(PARAM_KEY)) | |||
.setProfileName(wsRequest.param(PARAM_PROFILE)) | |||
.setIssuesMode(wsRequest.mandatoryParamAsBoolean(PARAM_ISSUES_MODE)) | |||
.setBranch(wsRequest.param(PARAM_BRANCH))); | |||
.setBranch(wsRequest.param(PARAM_BRANCH)) | |||
.setPullRequest(wsRequest.param(PARAM_PULL_REQUEST))); | |||
WsProjectResponse projectResponse = buildResponse(data); | |||
writeProtobuf(projectResponse, wsRequest, wsResponse); |
@@ -71,13 +71,15 @@ public class ProjectDataLoader { | |||
ProjectRepositories data = new ProjectRepositories(); | |||
String moduleKey = query.getModuleKey(); | |||
String branch = query.getBranch(); | |||
String pullRequest = query.getPullRequest(); | |||
ComponentDto mainModule = componentFinder.getByKey(session, moduleKey); | |||
checkRequest(isProjectOrModule(mainModule), "Key '%s' belongs to a component which is not a Project", moduleKey); | |||
boolean hasScanPerm = userSession.hasComponentPermission(SCAN_EXECUTION, mainModule) || | |||
userSession.hasPermission(OrganizationPermission.SCAN, mainModule.getOrganizationUuid()); | |||
boolean hasBrowsePerm = userSession.hasComponentPermission(USER, mainModule); | |||
checkPermission(query.isIssuesMode(), hasScanPerm, hasBrowsePerm); | |||
ComponentDto branchOrMainModule = branch == null ? mainModule : componentFinder.getByKeyAndBranch(session, moduleKey, branch); | |||
ComponentDto branchOrMainModule = (branch == null && pullRequest == null) ? mainModule | |||
: componentFinder.getByKeyAndOptionalBranchOrPullRequest(session, moduleKey, branch, pullRequest); | |||
ComponentDto project = getProject(branchOrMainModule, session); | |||
if (!project.getKey().equals(branchOrMainModule.getKey())) { |
@@ -28,6 +28,7 @@ public class ProjectDataQuery { | |||
private String profileName; | |||
private boolean issuesMode; | |||
private String branch; | |||
private String pullRequest; | |||
private ProjectDataQuery() { | |||
// No direct call | |||
@@ -71,6 +72,16 @@ public class ProjectDataQuery { | |||
return this; | |||
} | |||
@CheckForNull | |||
public String getPullRequest() { | |||
return pullRequest; | |||
} | |||
public ProjectDataQuery setPullRequest(@Nullable String pullRequest) { | |||
this.pullRequest = pullRequest; | |||
return this; | |||
} | |||
public static ProjectDataQuery create() { | |||
return new ProjectDataQuery(); | |||
} |
@@ -0,0 +1,93 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.branch.pr.ws; | |||
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.NewController; | |||
import org.sonar.api.web.UserRole; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.component.BranchDto; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.server.component.ComponentCleanerService; | |||
import org.sonar.server.component.ComponentFinder; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.user.UserSession; | |||
import static org.sonar.db.component.BranchType.PULL_REQUEST; | |||
import static org.sonar.server.branch.pr.ws.PullRequestsWs.addProjectParam; | |||
import static org.sonar.server.branch.pr.ws.PullRequestsWs.addPullRequestParam; | |||
import static org.sonar.server.branch.pr.ws.PullRequestsWsParameters.PARAM_PROJECT; | |||
import static org.sonar.server.branch.pr.ws.PullRequestsWsParameters.PARAM_PULL_REQUEST; | |||
import static org.sonar.server.branch.ws.ProjectBranchesParameters.ACTION_DELETE; | |||
public class DeleteAction implements PullRequestWsAction { | |||
private final DbClient dbClient; | |||
private final UserSession userSession; | |||
private final ComponentCleanerService componentCleanerService; | |||
private final ComponentFinder componentFinder; | |||
public DeleteAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, ComponentCleanerService componentCleanerService) { | |||
this.dbClient = dbClient; | |||
this.componentFinder = componentFinder; | |||
this.userSession = userSession; | |||
this.componentCleanerService = componentCleanerService; | |||
} | |||
@Override | |||
public void define(NewController context) { | |||
WebService.NewAction action = context.createAction(ACTION_DELETE) | |||
.setSince("7.1") | |||
.setDescription("Delete a pull request.<br/>" + | |||
"Requires 'Administer' rights on the specified project.") | |||
.setPost(true) | |||
.setHandler(this); | |||
addProjectParam(action); | |||
addPullRequestParam(action); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
userSession.checkLoggedIn(); | |||
String projectKey = request.mandatoryParam(PARAM_PROJECT); | |||
String pullRequestId = request.mandatoryParam(PARAM_PULL_REQUEST); | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
ComponentDto project = componentFinder.getRootComponentByUuidOrKey(dbSession, null, projectKey); | |||
checkPermission(project); | |||
BranchDto pullRequest = dbClient.branchDao().selectByPullRequestKey(dbSession, project.uuid(), pullRequestId) | |||
.filter(branch -> branch.getBranchType() == PULL_REQUEST) | |||
.orElseThrow(() -> new NotFoundException(String.format("Pull request '%s' is not found for project '%s'", pullRequestId, projectKey))); | |||
ComponentDto branchComponent = componentFinder.getByKeyAndPullRequest(dbSession, projectKey, pullRequest.getKey()); | |||
componentCleanerService.deleteBranch(dbSession, branchComponent); | |||
response.noContent(); | |||
} | |||
} | |||
private void checkPermission(ComponentDto project) { | |||
userSession.checkComponentPermission(UserRole.ADMIN, project); | |||
} | |||
} |
@@ -0,0 +1,142 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.branch.pr.ws; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Objects; | |||
import java.util.Optional; | |||
import java.util.function.Function; | |||
import javax.annotation.Nullable; | |||
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.web.UserRole; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.component.BranchDto; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.component.SnapshotDto; | |||
import org.sonar.db.protobuf.DbProjectBranches; | |||
import org.sonar.server.component.ComponentFinder; | |||
import org.sonar.server.issue.index.BranchStatistics; | |||
import org.sonar.server.issue.index.IssueIndex; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonarqube.ws.ProjectPullRequests; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static java.util.Objects.requireNonNull; | |||
import static org.sonar.api.resources.Qualifiers.PROJECT; | |||
import static org.sonar.api.utils.DateUtils.formatDateTime; | |||
import static org.sonar.core.util.Protobuf.setNullable; | |||
import static org.sonar.core.util.stream.MoreCollectors.toList; | |||
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex; | |||
import static org.sonar.db.component.BranchType.PULL_REQUEST; | |||
import static org.sonar.server.branch.pr.ws.PullRequestsWs.addProjectParam; | |||
import static org.sonar.server.branch.pr.ws.PullRequestsWsParameters.PARAM_PROJECT; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
public class ListAction implements PullRequestWsAction { | |||
private final DbClient dbClient; | |||
private final UserSession userSession; | |||
private final ComponentFinder componentFinder; | |||
private final IssueIndex issueIndex; | |||
public ListAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, IssueIndex issueIndex) { | |||
this.dbClient = dbClient; | |||
this.userSession = userSession; | |||
this.componentFinder = componentFinder; | |||
this.issueIndex = issueIndex; | |||
} | |||
@Override | |||
public void define(WebService.NewController context) { | |||
WebService.NewAction action = context.createAction("list") | |||
.setSince("7.1") | |||
.setDescription("List the pull requests of a project.<br/>" + | |||
"Requires 'Administer' rights on the specified project.") | |||
.setResponseExample(getClass().getResource("list-example.json")) | |||
.setHandler(this); | |||
addProjectParam(action); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
String projectKey = request.mandatoryParam(PARAM_PROJECT); | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
ComponentDto project = componentFinder.getByKey(dbSession, projectKey); | |||
userSession.checkComponentPermission(UserRole.USER, project); | |||
checkArgument(project.isEnabled() && PROJECT.equals(project.qualifier()), "Invalid project key"); | |||
List<BranchDto> pullRequests = dbClient.branchDao().selectByComponent(dbSession, project).stream() | |||
.filter(b -> b.getBranchType() == PULL_REQUEST) | |||
.collect(toList()); | |||
List<String> pullRequestUuids = pullRequests.stream().map(BranchDto::getUuid).collect(toList()); | |||
Map<String, BranchDto> mergeBranchesByUuid = dbClient.branchDao() | |||
.selectByUuids(dbSession, pullRequests.stream().map(BranchDto::getMergeBranchUuid).filter(Objects::nonNull).collect(toList())) | |||
.stream().collect(uniqueIndex(BranchDto::getUuid)); | |||
Map<String, BranchStatistics> branchStatisticsByBranchUuid = issueIndex.searchBranchStatistics(project.uuid(), pullRequestUuids).stream() | |||
.collect(uniqueIndex(BranchStatistics::getBranchUuid, Function.identity())); | |||
Map<String, String> analysisDateByBranchUuid = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, pullRequestUuids).stream() | |||
.collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> formatDateTime(s.getCreatedAt()))); | |||
ProjectPullRequests.ListWsResponse.Builder protobufResponse = ProjectPullRequests.ListWsResponse.newBuilder(); | |||
pullRequests | |||
.forEach(b -> addPullRequest(protobufResponse, b, mergeBranchesByUuid, branchStatisticsByBranchUuid.get(b.getUuid()), | |||
analysisDateByBranchUuid.get(b.getUuid()))); | |||
writeProtobuf(protobufResponse.build(), request, response); | |||
} | |||
} | |||
private static void addPullRequest(ProjectPullRequests.ListWsResponse.Builder response, BranchDto branch, Map<String, BranchDto> mergeBranchesByUuid, | |||
BranchStatistics branchStatistics, @Nullable String analysisDate) { | |||
Optional<BranchDto> mergeBranch = Optional.ofNullable(mergeBranchesByUuid.get(branch.getMergeBranchUuid())); | |||
ProjectPullRequests.PullRequest.Builder builder = ProjectPullRequests.PullRequest.newBuilder(); | |||
builder.setKey(branch.getKey()); | |||
DbProjectBranches.PullRequestData pullRequestData = requireNonNull(branch.getPullRequestData(), "Pull request data should be available for branch type PULL_REQUEST"); | |||
builder.setBranch(pullRequestData.getBranch()); | |||
builder.setUrl(pullRequestData.getUrl()); | |||
builder.setTitle(pullRequestData.getTitle()); | |||
if (mergeBranch.isPresent()) { | |||
String mergeBranchKey = mergeBranch.get().getKey(); | |||
builder.setBase(mergeBranchKey); | |||
} else { | |||
builder.setIsOrphan(true); | |||
} | |||
setNullable(analysisDate, builder::setAnalysisDate); | |||
setBranchStatus(builder, branchStatistics); | |||
response.addPullRequests(builder); | |||
} | |||
private static void setBranchStatus(ProjectPullRequests.PullRequest.Builder builder, @Nullable BranchStatistics branchStatistics) { | |||
ProjectPullRequests.Status.Builder statusBuilder = ProjectPullRequests.Status.newBuilder(); | |||
statusBuilder.setBugs(branchStatistics == null ? 0L : branchStatistics.getBugs()); | |||
statusBuilder.setVulnerabilities(branchStatistics == null ? 0L : branchStatistics.getVulnerabilities()); | |||
statusBuilder.setCodeSmells(branchStatistics == null ? 0L : branchStatistics.getCodeSmells()); | |||
builder.setStatus(statusBuilder); | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.branch.pr.ws; | |||
import org.sonar.server.ws.WsAction; | |||
public interface PullRequestWsAction extends WsAction { | |||
// marker interface | |||
} |
@@ -0,0 +1,32 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.branch.pr.ws; | |||
import org.sonar.core.platform.Module; | |||
public class PullRequestWsModule extends Module { | |||
@Override | |||
protected void configureModule() { | |||
add( | |||
ListAction.class, | |||
DeleteAction.class, | |||
PullRequestsWs.class); | |||
} | |||
} |
@@ -0,0 +1,61 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.branch.pr.ws; | |||
import org.sonar.api.server.ws.WebService; | |||
import static java.util.Arrays.stream; | |||
import static org.sonar.server.branch.pr.ws.PullRequestsWsParameters.PARAM_PROJECT; | |||
import static org.sonar.server.branch.pr.ws.PullRequestsWsParameters.PARAM_PULL_REQUEST; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
public class PullRequestsWs implements WebService { | |||
private final PullRequestWsAction[] actions; | |||
public PullRequestsWs(PullRequestWsAction... actions) { | |||
this.actions = actions; | |||
} | |||
@Override | |||
public void define(Context context) { | |||
NewController controller = context.createController("api/project_pull_requests") | |||
.setSince("7.1") | |||
.setDescription("Manage pull request (only available when the Branch plugin is installed)"); | |||
stream(actions).forEach(action -> action.define(controller)); | |||
controller.done(); | |||
} | |||
static void addProjectParam(NewAction action) { | |||
action | |||
.createParam(PARAM_PROJECT) | |||
.setDescription("Project key") | |||
.setExampleValue(KEY_PROJECT_EXAMPLE_001) | |||
.setRequired(true); | |||
} | |||
static void addPullRequestParam(NewAction action) { | |||
action | |||
.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setExampleValue("1543") | |||
.setRequired(true); | |||
} | |||
} |
@@ -0,0 +1,31 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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.branch.pr.ws; | |||
public class PullRequestsWsParameters { | |||
public static final String PARAM_PROJECT = "project"; | |||
public static final String PARAM_COMPONENT = "component"; | |||
public static final String PARAM_PULL_REQUEST = "pullRequest"; | |||
private PullRequestsWsParameters() { | |||
// static utility class | |||
} | |||
} |
@@ -0,0 +1,23 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2018 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. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.server.branch.pr.ws; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -19,7 +19,6 @@ | |||
*/ | |||
package org.sonar.server.branch.ws; | |||
import com.google.common.io.Resources; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
@@ -35,10 +34,10 @@ import org.sonar.server.user.UserSession; | |||
import static org.sonar.server.branch.ws.BranchesWs.addBranchParam; | |||
import static org.sonar.server.branch.ws.BranchesWs.addProjectParam; | |||
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; | |||
import static org.sonar.server.branch.ws.ProjectBranchesParameters.ACTION_DELETE; | |||
import static org.sonar.server.branch.ws.ProjectBranchesParameters.PARAM_BRANCH; | |||
import static org.sonar.server.branch.ws.ProjectBranchesParameters.PARAM_PROJECT; | |||
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; | |||
public class DeleteAction implements BranchWsAction { | |||
private final DbClient dbClient; | |||
@@ -59,7 +58,6 @@ public class DeleteAction implements BranchWsAction { | |||
.setSince("6.6") | |||
.setDescription("Delete a non-main branch of a project.<br/>" + | |||
"Requires 'Administer' rights on the specified project.") | |||
.setResponseExample(Resources.getResource(getClass(), "list-example.json")) | |||
.setPost(true) | |||
.setHandler(this); | |||
@@ -78,7 +76,7 @@ public class DeleteAction implements BranchWsAction { | |||
checkPermission(project); | |||
BranchDto branch = checkFoundWithOptional( | |||
dbClient.branchDao().selectByKey(dbSession, project.uuid(), branchKey), | |||
dbClient.branchDao().selectByBranchKey(dbSession, project.uuid(), branchKey), | |||
"Branch '%s' not found for project '%s'", branchKey, projectKey); | |||
if (branch.isMain()) { |
@@ -21,17 +21,18 @@ package org.sonar.server.branch.ws; | |||
import com.google.common.io.Resources; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Objects; | |||
import java.util.Optional; | |||
import java.util.function.Function; | |||
import java.util.stream.Collectors; | |||
import javax.annotation.Nullable; | |||
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.web.UserRole; | |||
import org.sonar.core.util.Protobuf; | |||
import org.sonar.core.util.stream.MoreCollectors; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.component.BranchDto; | |||
@@ -99,30 +100,34 @@ public class ListAction implements BranchWsAction { | |||
checkPermission(project); | |||
checkArgument(project.isEnabled() && PROJECT.equals(project.qualifier()), "Invalid project key"); | |||
Collection<BranchDto> branches = dbClient.branchDao().selectByComponent(dbSession, project); | |||
Collection<BranchDto> branches = dbClient.branchDao().selectByComponent(dbSession, project).stream() | |||
.filter(b -> b.getBranchType() == SHORT || b.getBranchType() == LONG) | |||
.collect(MoreCollectors.toList()); | |||
List<String> branchUuids = branches.stream().map(BranchDto::getUuid).collect(toList()); | |||
Map<String, BranchDto> mergeBranchesByUuid = dbClient.branchDao() | |||
.selectByUuids(dbSession, branches.stream().map(BranchDto::getMergeBranchUuid).filter(Objects::nonNull).collect(toList())) | |||
.stream().collect(uniqueIndex(BranchDto::getUuid)); | |||
Map<String, LiveMeasureDto> qualityGateMeasuresByComponentUuids = dbClient.liveMeasureDao() | |||
.selectByComponentUuidsAndMetricKeys(dbSession, branches.stream().map(BranchDto::getUuid).collect(toList()), singletonList(ALERT_STATUS_KEY)) | |||
.stream().collect(uniqueIndex(LiveMeasureDto::getComponentUuid)); | |||
.selectByComponentUuidsAndMetricKeys(dbSession, branchUuids, singletonList(ALERT_STATUS_KEY)).stream() | |||
.collect(uniqueIndex(LiveMeasureDto::getComponentUuid)); | |||
Map<String, BranchStatistics> branchStatisticsByBranchUuid = issueIndex.searchBranchStatistics(project.uuid(), branches.stream() | |||
.filter(b -> b.getBranchType().equals(SHORT)) | |||
.map(BranchDto::getUuid).collect(toList())) | |||
.stream().collect(uniqueIndex(BranchStatistics::getBranchUuid, Function.identity())); | |||
.map(BranchDto::getUuid).collect(toList())).stream() | |||
.collect(uniqueIndex(BranchStatistics::getBranchUuid, Function.identity())); | |||
Map<String, String> analysisDateByBranchUuid = dbClient.snapshotDao() | |||
.selectLastAnalysesByRootComponentUuids(dbSession, branches.stream().map(BranchDto::getUuid).collect(Collectors.toList())) | |||
.stream().collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> formatDateTime(s.getCreatedAt()))); | |||
.selectLastAnalysesByRootComponentUuids(dbSession, branchUuids).stream() | |||
.collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> formatDateTime(s.getCreatedAt()))); | |||
ProjectBranches.ListWsResponse.Builder protobufResponse = ProjectBranches.ListWsResponse.newBuilder(); | |||
branches.forEach(b -> addBranch(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid()), branchStatisticsByBranchUuid.get(b.getUuid()), | |||
analysisDateByBranchUuid.get(b.getUuid()))); | |||
analysisDateByBranchUuid.get(b.getUuid()))); | |||
WsUtils.writeProtobuf(protobufResponse.build(), request, response); | |||
} | |||
} | |||
private static void addBranch(ProjectBranches.ListWsResponse.Builder response, BranchDto branch, Map<String, BranchDto> mergeBranchesByUuid, | |||
@Nullable LiveMeasureDto qualityGateMeasure, BranchStatistics branchStatistics, @Nullable String analysisDate) { | |||
@Nullable LiveMeasureDto qualityGateMeasure, BranchStatistics branchStatistics, @Nullable String analysisDate) { | |||
ProjectBranches.Branch.Builder builder = toBranchBuilder(branch, Optional.ofNullable(mergeBranchesByUuid.get(branch.getMergeBranchUuid()))); | |||
setBranchStatus(builder, branch, qualityGateMeasure, branchStatistics); | |||
if (analysisDate != null) { | |||
@@ -137,7 +142,7 @@ public class ListAction implements BranchWsAction { | |||
setNullable(branchKey, builder::setName); | |||
builder.setIsMain(branch.isMain()); | |||
builder.setType(Common.BranchType.valueOf(branch.getBranchType().name())); | |||
if (branch.getBranchType().equals(SHORT)) { | |||
if (branch.getBranchType() == SHORT) { | |||
if (mergeBranch.isPresent()) { | |||
String mergeBranchKey = mergeBranch.get().getKey(); | |||
builder.setMergeBranch(mergeBranchKey); | |||
@@ -149,8 +154,8 @@ public class ListAction implements BranchWsAction { | |||
} | |||
private static void setBranchStatus(ProjectBranches.Branch.Builder builder, BranchDto branch, @Nullable LiveMeasureDto qualityGateMeasure, | |||
@Nullable BranchStatistics branchStatistics) { | |||
ProjectBranches.Branch.Status.Builder statusBuilder = ProjectBranches.Branch.Status.newBuilder(); | |||
@Nullable BranchStatistics branchStatistics) { | |||
ProjectBranches.Status.Builder statusBuilder = ProjectBranches.Status.newBuilder(); | |||
if (branch.getBranchType() == LONG && qualityGateMeasure != null) { | |||
Protobuf.setNullable(qualityGateMeasure.getDataAsString(), statusBuilder::setQualityGateStatus); | |||
} |
@@ -77,7 +77,7 @@ public class RenameAction implements BranchWsAction { | |||
ComponentDto project = componentFinder.getRootComponentByUuidOrKey(dbSession, null, projectKey); | |||
checkPermission(project); | |||
Optional<BranchDto> existingBranch = dbClient.branchDao().selectByKey(dbSession, project.uuid(), newBranchName); | |||
Optional<BranchDto> existingBranch = dbClient.branchDao().selectByBranchKey(dbSession, project.uuid(), newBranchName); | |||
checkArgument(!existingBranch.filter(b -> !b.isMain()).isPresent(), | |||
"Impossible to update branch name: a branch with name \"%s\" already exists in the project.", newBranchName); | |||
@@ -100,7 +100,8 @@ public class ActivityAction implements CeWsAction { | |||
.setChangelog( | |||
new Change("5.5", "it's no more possible to specify the page parameter."), | |||
new Change("6.1", "field \"logs\" is deprecated and its value is always false"), | |||
new Change("6.6", "fields \"branch\" and \"branchType\" added")) | |||
new Change("6.6", "fields \"branch\" and \"branchType\" added"), | |||
new Change("7.1", "fields \"pullRequest\" and \"pullRequestTitle\" added")) | |||
.setSince("5.2"); | |||
action.createParam(PARAM_COMPONENT_ID) |
@@ -89,7 +89,7 @@ public class TaskFormatter { | |||
builder.setSubmittedAt(formatDateTime(new Date(dto.getCreatedAt()))); | |||
setNullable(dto.getStartedAt(), builder::setStartedAt, DateUtils::formatDateTime); | |||
setNullable(computeExecutionTimeMs(dto), builder::setExecutionTimeMs); | |||
setBranch(builder, dto.getUuid(), componentDtoCache); | |||
setBranchOrPullRequest(builder, dto.getUuid(), componentDtoCache); | |||
return builder.build(); | |||
} | |||
@@ -115,10 +115,8 @@ public class TaskFormatter { | |||
builder.setLogs(false); | |||
setNullable(dto.getComponentUuid(), uuid -> setComponent(builder, uuid, componentDtoCache).setComponentId(uuid)); | |||
String analysisUuid = dto.getAnalysisUuid(); | |||
if (analysisUuid != null) { | |||
builder.setAnalysisId(analysisUuid); | |||
} | |||
setBranch(builder, dto.getUuid(), componentDtoCache); | |||
setNullable(analysisUuid, builder::setAnalysisId); | |||
setBranchOrPullRequest(builder, dto.getUuid(), componentDtoCache); | |||
setNullable(analysisUuid, builder::setAnalysisId); | |||
setNullable(dto.getSubmitterLogin(), builder::setSubmitterLogin); | |||
builder.setSubmittedAt(formatDateTime(new Date(dto.getSubmittedAt()))); | |||
@@ -144,13 +142,22 @@ public class TaskFormatter { | |||
return builder; | |||
} | |||
private static Ce.Task.Builder setBranch(Ce.Task.Builder builder, String taskUuid, DtoCache componentDtoCache) { | |||
componentDtoCache.getBranchName(taskUuid).ifPresent( | |||
private static Ce.Task.Builder setBranchOrPullRequest(Ce.Task.Builder builder, String taskUuid, DtoCache componentDtoCache) { | |||
componentDtoCache.getBranchKey(taskUuid).ifPresent( | |||
b -> { | |||
builder.setBranch(b); | |||
builder.setBranchType(componentDtoCache.getBranchType(taskUuid) | |||
.orElseThrow(() -> new IllegalStateException(format("Could not find branch type of task '%s'", taskUuid)))); | |||
Common.BranchType branchType = componentDtoCache.getBranchType(taskUuid) | |||
.orElseThrow(() -> new IllegalStateException(format("Could not find branch type of task '%s'", taskUuid))); | |||
switch (branchType) { | |||
case LONG: | |||
case SHORT: | |||
builder.setBranchType(branchType); | |||
builder.setBranch(b); | |||
break; | |||
default: | |||
throw new IllegalStateException(String.format("Unknown branch type '%s'", branchType)); | |||
} | |||
}); | |||
componentDtoCache.getPullRequest(taskUuid).ifPresent(builder::setPullRequest); | |||
return builder; | |||
} | |||
@@ -237,7 +244,7 @@ public class TaskFormatter { | |||
return organizationDto.getKey(); | |||
} | |||
Optional<String> getBranchName(String taskUuid) { | |||
Optional<String> getBranchKey(String taskUuid) { | |||
return characteristicsByTaskUuid.get(taskUuid).stream() | |||
.filter(c -> c.getKey().equals(CeTaskCharacteristicDto.BRANCH_KEY)) | |||
.map(CeTaskCharacteristicDto::getValue) | |||
@@ -250,6 +257,13 @@ public class TaskFormatter { | |||
.map(c -> Common.BranchType.valueOf(c.getValue())) | |||
.findAny(); | |||
} | |||
Optional<String> getPullRequest(String taskUuid) { | |||
return characteristicsByTaskUuid.get(taskUuid).stream() | |||
.filter(c -> c.getKey().equals(CeTaskCharacteristicDto.PULL_REQUEST)) | |||
.map(CeTaskCharacteristicDto::getValue) | |||
.findAny(); | |||
} | |||
} | |||
/** |
@@ -154,8 +154,24 @@ public class ComponentFinder { | |||
throw new NotFoundException(format("Component '%s' on branch '%s' not found", key, branch)); | |||
} | |||
public ComponentDto getByKeyAndOptionalBranch(DbSession dbSession, String key, @Nullable String branch) { | |||
return branch == null ? getByKey(dbSession, key) : getByKeyAndBranch(dbSession, key, branch); | |||
public ComponentDto getByKeyAndPullRequest(DbSession dbSession, String key, String pullRequest) { | |||
java.util.Optional<ComponentDto> componentDto = dbClient.componentDao().selectByKeyAndPullRequest(dbSession, key, pullRequest); | |||
if (componentDto.isPresent() && componentDto.get().isEnabled()) { | |||
return componentDto.get(); | |||
} | |||
throw new NotFoundException(format("Component '%s' of pull request '%s' not found", key, pullRequest)); | |||
} | |||
public ComponentDto getByKeyAndOptionalBranchOrPullRequest(DbSession dbSession, String key, @Nullable String branch, @Nullable String pullRequest) { | |||
checkArgument(branch == null || pullRequest == null, "Either branch or pull request can be provided, not both"); | |||
if (branch != null) { | |||
return getByKeyAndBranch(dbSession, key, branch); | |||
} | |||
if (pullRequest != null) { | |||
return getByKeyAndPullRequest(dbSession, key, pullRequest); | |||
} | |||
return getByKey(dbSession, key); | |||
} | |||
public enum ParamNames { | |||
@@ -165,7 +181,7 @@ public class ComponentFinder { | |||
UUID_AND_KEY("uuid", "key"), | |||
ID_AND_KEY("id", "key"), | |||
COMPONENT_ID_AND_KEY("componentId", "componentKey"), | |||
BASE_COMPONENT_ID_AND_KEY("baseComponentId", "baseComponentKey"), | |||
BASE_COMPONENT_ID_AND_KEY("baseComponentId", "component"), | |||
DEVELOPER_ID_AND_KEY("developerId", "developerKey"), | |||
COMPONENT_ID_AND_COMPONENT("componentId", "component"), | |||
PROJECT_ID_AND_PROJECT("projectId", "project"), |
@@ -57,9 +57,11 @@ import static org.sonar.api.measures.CoreMetrics.VIOLATIONS; | |||
import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY; | |||
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01; | |||
import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
public class AppAction implements ComponentsWsAction { | |||
@@ -110,6 +112,12 @@ public class AppAction implements ComponentsWsAction { | |||
.setSince("6.6") | |||
.setInternal(true) | |||
.setExampleValue(KEY_BRANCH_EXAMPLE_001); | |||
action.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setSince("7.1") | |||
.setInternal(true) | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001); | |||
} | |||
@Override | |||
@@ -131,12 +139,14 @@ public class AppAction implements ComponentsWsAction { | |||
private ComponentDto loadComponent(DbSession dbSession, Request request) { | |||
String componentUuid = request.param(PARAM_COMPONENT_ID); | |||
String branch = request.param("branch"); | |||
checkArgument(componentUuid == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", PARAM_COMPONENT_ID, PARAM_BRANCH); | |||
if (branch == null) { | |||
String branch = request.param(PARAM_BRANCH); | |||
String pullRequest = request.param(PARAM_PULL_REQUEST); | |||
checkArgument(componentUuid == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'", PARAM_COMPONENT_ID, | |||
PARAM_BRANCH, PARAM_PULL_REQUEST); | |||
if (branch == null && pullRequest == null) { | |||
return componentFinder.getByUuidOrKey(dbSession, componentUuid, request.param(PARAM_COMPONENT), COMPONENT_ID_AND_COMPONENT); | |||
} | |||
return componentFinder.getByKeyAndOptionalBranch(dbSession, request.mandatoryParam(PARAM_COMPONENT), branch); | |||
return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, request.mandatoryParam(PARAM_COMPONENT), branch, pullRequest); | |||
} | |||
private void appendComponent(JsonWriter json, ComponentDto component, UserSession userSession, DbSession session) { | |||
@@ -168,6 +178,10 @@ public class AppAction implements ComponentsWsAction { | |||
if (branch != null) { | |||
json.prop("branch", branch); | |||
} | |||
String pullRequest = project.getPullRequest(); | |||
if (pullRequest != null) { | |||
json.prop("pullRequest", pullRequest); | |||
} | |||
json.prop("fav", isFavourite); | |||
} |
@@ -62,6 +62,7 @@ class ComponentDtoToWsComponent { | |||
.setName(dto.name()) | |||
.setQualifier(dto.qualifier()); | |||
setNullable(emptyToNull(dto.getBranch()), wsComponent::setBranch); | |||
setNullable(emptyToNull(dto.getPullRequest()), wsComponent::setPullRequest); | |||
setNullable(emptyToNull(dto.path()), wsComponent::setPath); | |||
setNullable(emptyToNull(dto.description()), wsComponent::setDescription); | |||
setNullable(emptyToNull(dto.language()), wsComponent::setLanguage); |
@@ -35,6 +35,7 @@ public class MeasuresWsParameters { | |||
public static final String DEPRECATED_PARAM_BASE_COMPONENT_KEY = "baseComponentKey"; | |||
public static final String PARAM_COMPONENT = "component"; | |||
public static final String PARAM_BRANCH = "branch"; | |||
public static final String PARAM_PULL_REQUEST = "pullRequest"; | |||
public static final String PARAM_STRATEGY = "strategy"; | |||
public static final String PARAM_QUALIFIERS = "qualifiers"; | |||
public static final String PARAM_METRICS = "metrics"; |
@@ -22,6 +22,8 @@ package org.sonar.server.component.ws; | |||
import java.util.List; | |||
import java.util.Optional; | |||
import java.util.stream.IntStream; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.server.ws.Change; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
@@ -35,21 +37,21 @@ import org.sonar.server.component.ComponentFinder; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonarqube.ws.Components.ShowWsResponse; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static java.lang.String.format; | |||
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01; | |||
import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT; | |||
import static org.sonar.server.component.ws.ComponentDtoToWsComponent.componentDtoToWsComponent; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.checkRequest; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SHOW; | |||
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT; | |||
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT_ID; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH; | |||
public class ShowAction implements ComponentsWsAction { | |||
private final UserSession userSession; | |||
@@ -97,6 +99,12 @@ public class ShowAction implements ComponentsWsAction { | |||
.setExampleValue(KEY_BRANCH_EXAMPLE_001) | |||
.setInternal(true) | |||
.setSince("6.6"); | |||
action.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001) | |||
.setInternal(true) | |||
.setSince("7.1"); | |||
} | |||
@Override | |||
@@ -110,6 +118,7 @@ public class ShowAction implements ComponentsWsAction { | |||
private ShowWsResponse doHandle(Request request) { | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
ComponentDto component = loadComponent(dbSession, request); | |||
userSession.checkComponentPermission(UserRole.USER, component); | |||
Optional<SnapshotDto> lastAnalysis = dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, component.projectUuid()); | |||
List<ComponentDto> ancestors = dbClient.componentDao().selectAncestors(dbSession, component); | |||
OrganizationDto organizationDto = componentFinder.getOrganization(dbSession, component); | |||
@@ -121,12 +130,14 @@ public class ShowAction implements ComponentsWsAction { | |||
String componentId = request.getId(); | |||
String componentKey = request.getKey(); | |||
String branch = request.getBranch(); | |||
checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", PARAM_COMPONENT_ID, PARAM_BRANCH); | |||
ComponentDto component = branch == null | |||
? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT) | |||
: componentFinder.getByKeyAndBranch(dbSession, componentKey, branch); | |||
userSession.checkComponentPermission(UserRole.USER, component); | |||
return component; | |||
String pullRequest = request.getPullRequest(); | |||
checkArgument(componentId == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'", PARAM_COMPONENT_ID, | |||
PARAM_BRANCH, PARAM_PULL_REQUEST); | |||
if (branch == null && pullRequest == null) { | |||
return componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT); | |||
} | |||
checkRequest(componentKey!=null, "The '%s' parameter is missing", PARAM_COMPONENT); | |||
return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest); | |||
} | |||
private static ShowWsResponse buildResponse(ComponentDto component, OrganizationDto organizationDto, List<ComponentDto> orderedAncestors, Optional<SnapshotDto> lastAnalysis) { | |||
@@ -144,13 +155,15 @@ public class ShowAction implements ComponentsWsAction { | |||
return new Request() | |||
.setId(request.param(PARAM_COMPONENT_ID)) | |||
.setKey(request.param(PARAM_COMPONENT)) | |||
.setBranch(request.param(PARAM_BRANCH)); | |||
.setBranch(request.param(PARAM_BRANCH)) | |||
.setPullRequest(request.param(PARAM_PULL_REQUEST)); | |||
} | |||
private static class Request { | |||
private String id; | |||
private String key; | |||
private String branch; | |||
private String pullRequest; | |||
@CheckForNull | |||
public String getId() { | |||
@@ -181,5 +194,15 @@ public class ShowAction implements ComponentsWsAction { | |||
this.branch = branch; | |||
return this; | |||
} | |||
@CheckForNull | |||
public String getPullRequest() { | |||
return pullRequest; | |||
} | |||
public Request setPullRequest(@Nullable String pullRequest) { | |||
this.pullRequest = pullRequest; | |||
return this; | |||
} | |||
} | |||
} |
@@ -66,15 +66,18 @@ import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID | |||
import static org.sonar.server.component.ws.ComponentDtoToWsComponent.componentDtoToWsComponent; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext; | |||
import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter; | |||
import static org.sonar.server.ws.WsUtils.checkRequest; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_TREE; | |||
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BRANCH; | |||
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT; | |||
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT_ID; | |||
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PULL_REQUEST; | |||
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUALIFIERS; | |||
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_STRATEGY; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH; | |||
public class TreeAction implements ComponentsWsAction { | |||
@@ -138,6 +141,12 @@ public class TreeAction implements ComponentsWsAction { | |||
.setInternal(true) | |||
.setSince("6.6"); | |||
action.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001) | |||
.setInternal(true) | |||
.setSince("7.1"); | |||
action.createSortParams(SORTS, NAME_SORT, true) | |||
.setDescription("Comma-separated list of sort fields") | |||
.setExampleValue(NAME_SORT + ", " + PATH_SORT); | |||
@@ -190,12 +199,16 @@ public class TreeAction implements ComponentsWsAction { | |||
private ComponentDto loadComponent(DbSession dbSession, Request request) { | |||
String componentId = request.getBaseComponentId(); | |||
String componentKey = request.getBaseComponentKey(); | |||
String componentKey = request.getComponent(); | |||
String branch = request.getBranch(); | |||
checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", PARAM_COMPONENT_ID, PARAM_BRANCH); | |||
return branch == null | |||
? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT) | |||
: componentFinder.getByKeyAndBranch(dbSession, componentKey, branch); | |||
String pullRequest = request.getPullRequest(); | |||
checkArgument(componentId == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'", PARAM_COMPONENT_ID, | |||
PARAM_BRANCH, PARAM_PULL_REQUEST); | |||
if (branch == null && pullRequest == null) { | |||
return componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT); | |||
} | |||
checkRequest(componentKey != null, "The '%s' parameter is missing", PARAM_COMPONENT); | |||
return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest); | |||
} | |||
private Map<String, ComponentDto> searchReferenceComponentsByUuid(DbSession dbSession, List<ComponentDto> components) { | |||
@@ -285,8 +298,9 @@ public class TreeAction implements ComponentsWsAction { | |||
private static Request toTreeWsRequest(org.sonar.api.server.ws.Request request) { | |||
return new Request() | |||
.setBaseComponentId(request.param(PARAM_COMPONENT_ID)) | |||
.setBaseComponentKey(request.param(PARAM_COMPONENT)) | |||
.setComponent(request.param(PARAM_COMPONENT)) | |||
.setBranch(request.param(PARAM_BRANCH)) | |||
.setPullRequest(request.param(PARAM_PULL_REQUEST)) | |||
.setStrategy(request.mandatoryParam(PARAM_STRATEGY)) | |||
.setQuery(request.param(Param.TEXT_QUERY)) | |||
.setQualifiers(request.paramAsStrings(PARAM_QUALIFIERS)) | |||
@@ -335,9 +349,9 @@ public class TreeAction implements ComponentsWsAction { | |||
private static class Request { | |||
private String baseComponentId; | |||
private String baseComponentKey; | |||
private String component; | |||
private String branch; | |||
private String pullRequest; | |||
private String strategy; | |||
private List<String> qualifiers; | |||
private String query; | |||
@@ -364,24 +378,6 @@ public class TreeAction implements ComponentsWsAction { | |||
return this; | |||
} | |||
/** | |||
* @deprecated since 6.4, please use {@link #getComponent()} instead | |||
*/ | |||
@Deprecated | |||
@CheckForNull | |||
private String getBaseComponentKey() { | |||
return baseComponentKey; | |||
} | |||
/** | |||
* @deprecated since 6.4, please use {@link #setComponent(String)} instead | |||
*/ | |||
@Deprecated | |||
private Request setBaseComponentKey(@Nullable String baseComponentKey) { | |||
this.baseComponentKey = baseComponentKey; | |||
return this; | |||
} | |||
public Request setComponent(@Nullable String component) { | |||
this.component = component; | |||
return this; | |||
@@ -402,6 +398,16 @@ public class TreeAction implements ComponentsWsAction { | |||
return this; | |||
} | |||
@CheckForNull | |||
public String getPullRequest() { | |||
return pullRequest; | |||
} | |||
public Request setPullRequest(@Nullable String pullRequest) { | |||
this.pullRequest = pullRequest; | |||
return this; | |||
} | |||
@CheckForNull | |||
private String getStrategy() { | |||
return strategy; |
@@ -82,6 +82,13 @@ public interface AnalysisMetadataHolder { | |||
*/ | |||
boolean isLongLivingBranch(); | |||
/** | |||
* Convenience method equivalent to do the check using {@link #getBranch()} | |||
* | |||
* @throws IllegalStateException if branch has not been set | |||
*/ | |||
boolean isPullRequest(); | |||
/** | |||
* @throws IllegalStateException if cross project duplication flag has not been set | |||
*/ | |||
@@ -92,6 +99,13 @@ public interface AnalysisMetadataHolder { | |||
*/ | |||
Branch getBranch(); | |||
/** | |||
* In a pull request analysis, return the ID of the pull request | |||
* | |||
* @throws IllegalStateException if current analysis is not a pull request | |||
*/ | |||
String getPullRequestId(); | |||
/** | |||
* The project as represented by the main branch. It is used to load settings | |||
* like Quality gates, webhooks and configuration. | |||
@@ -119,5 +133,4 @@ public interface AnalysisMetadataHolder { | |||
* Plugins used during the analysis on scanner side | |||
*/ | |||
Map<String, ScannerPlugin> getScannerPluginsByKey(); | |||
} |
@@ -39,6 +39,7 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder | |||
private final InitializedProperty<Analysis> baseProjectSnapshot = new InitializedProperty<>(); | |||
private final InitializedProperty<Boolean> crossProjectDuplicationEnabled = new InitializedProperty<>(); | |||
private final InitializedProperty<Branch> branch = new InitializedProperty<>(); | |||
private final InitializedProperty<String> pullRequestId = new InitializedProperty<>(); | |||
private final InitializedProperty<Project> project = new InitializedProperty<>(); | |||
private final InitializedProperty<Integer> rootComponentRef = new InitializedProperty<>(); | |||
private final InitializedProperty<Map<String, QualityProfile>> qProfilesPerLanguage = new InitializedProperty<>(); | |||
@@ -148,6 +149,19 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder | |||
return branch.getProperty(); | |||
} | |||
@Override | |||
public MutableAnalysisMetadataHolder setPullRequestId(String pullRequestId) { | |||
checkState(!this.pullRequestId.isInitialized(), "Pull request id has already been set"); | |||
this.pullRequestId.setProperty(pullRequestId); | |||
return this; | |||
} | |||
@Override | |||
public String getPullRequestId() { | |||
checkState(pullRequestId.isInitialized(), "Pull request id has not been set"); | |||
return pullRequestId.getProperty(); | |||
} | |||
@Override | |||
public MutableAnalysisMetadataHolder setProject(Project project) { | |||
checkState(!this.project.isInitialized(), "Project has already been set"); | |||
@@ -201,16 +215,25 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder | |||
return pluginsByKey.getProperty(); | |||
} | |||
@Override | |||
public boolean isShortLivingBranch() { | |||
checkState(this.branch.isInitialized(), BRANCH_NOT_SET); | |||
Branch prop = branch.getProperty(); | |||
return prop != null && prop.getType() == BranchType.SHORT; | |||
} | |||
@Override | |||
public boolean isLongLivingBranch() { | |||
checkState(this.branch.isInitialized(), BRANCH_NOT_SET); | |||
Branch prop = branch.getProperty(); | |||
return prop != null && prop.getType() == BranchType.LONG; | |||
} | |||
@Override | |||
public boolean isPullRequest() { | |||
checkState(this.branch.isInitialized(), BRANCH_NOT_SET); | |||
Branch prop = branch.getProperty(); | |||
return prop != null && prop.getType() == BranchType.PULL_REQUEST; | |||
} | |||
} |
@@ -53,4 +53,9 @@ public interface Branch extends ComponentKeyGenerator { | |||
* or not. | |||
*/ | |||
boolean supportsCrossProjectCpd(); | |||
/** | |||
* @throws IllegalStateException if this branch configuration is not a pull request. | |||
*/ | |||
String getPullRequestId(); | |||
} |
@@ -60,6 +60,11 @@ public interface MutableAnalysisMetadataHolder extends AnalysisMetadataHolder { | |||
*/ | |||
MutableAnalysisMetadataHolder setBranch(Branch branch); | |||
/** | |||
* @throws IllegalStateException if pull request id has already been set | |||
*/ | |||
MutableAnalysisMetadataHolder setPullRequestId(String pullRequestId); | |||
/** | |||
* @throws IllegalStateException if project has already been set | |||
*/ |
@@ -54,6 +54,7 @@ import static java.util.Optional.of; | |||
import static java.util.Optional.ofNullable; | |||
import static org.sonar.api.ce.posttask.CeTask.Status.FAILED; | |||
import static org.sonar.api.ce.posttask.CeTask.Status.SUCCESS; | |||
import static org.sonar.db.component.BranchType.PULL_REQUEST; | |||
/** | |||
* Responsible for calling {@link PostProjectAnalysisTask} implementations (if any). | |||
@@ -180,7 +181,8 @@ public class PostProjectAnalysisTasksExecutor implements ComputationStepExecutor | |||
private BranchImpl createBranch() { | |||
org.sonar.server.computation.task.projectanalysis.analysis.Branch analysisBranch = analysisMetadataHolder.getBranch(); | |||
if (!analysisBranch.isLegacyFeature()) { | |||
return new BranchImpl(analysisBranch.isMain(), analysisBranch.getName(), Branch.Type.valueOf(analysisBranch.getType().name())); | |||
String branchKey = analysisBranch.getType() == PULL_REQUEST ? analysisBranch.getPullRequestId() : analysisBranch.getName(); | |||
return new BranchImpl(analysisBranch.isMain(), branchKey, Branch.Type.valueOf(analysisBranch.getType().name())); | |||
} | |||
return null; | |||
} |
@@ -25,7 +25,9 @@ import org.sonar.api.utils.System2; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.component.BranchDto; | |||
import org.sonar.db.component.BranchType; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.protobuf.DbProjectBranches; | |||
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder; | |||
import org.sonar.server.computation.task.projectanalysis.analysis.Branch; | |||
@@ -55,12 +57,11 @@ public class BranchPersisterImpl implements BranchPersister { | |||
if (branch.isMain()) { | |||
checkState(branchComponentDtoOpt.isPresent(), "Project has been deleted by end-user during analysis"); | |||
branchComponentDto = branchComponentDtoOpt.get(); | |||
} else { | |||
// inserts new row in table projects if it's the first time branch is analyzed | |||
branchComponentDto = branchComponentDtoOpt.or(() -> insertIntoProjectsTable(dbSession, branchUuid)); | |||
} | |||
// insert or update in table project_branches | |||
dbClient.branchDao().upsert(dbSession, toBranchDto(branchComponentDto, branch)); | |||
} | |||
@@ -75,15 +76,29 @@ public class BranchPersisterImpl implements BranchPersister { | |||
return (first != null) ? first : second; | |||
} | |||
private static BranchDto toBranchDto(ComponentDto componentDto, Branch branch) { | |||
private BranchDto toBranchDto(ComponentDto componentDto, Branch branch) { | |||
BranchDto dto = new BranchDto(); | |||
dto.setUuid(componentDto.uuid()); | |||
// MainBranchProjectUuid will be null if it's a main branch | |||
dto.setProjectUuid(firstNonNull(componentDto.getMainBranchProjectUuid(), componentDto.projectUuid())); | |||
dto.setKey(branch.getName()); | |||
dto.setBranchType(branch.getType()); | |||
// merge branch is only present if it's a short living branch | |||
dto.setMergeBranchUuid(branch.getMergeBranchUuid().orElse(null)); | |||
if (branch.getType() == BranchType.PULL_REQUEST) { | |||
dto.setKey(analysisMetadataHolder.getPullRequestId()); | |||
DbProjectBranches.PullRequestData pullRequestData = DbProjectBranches.PullRequestData.newBuilder() | |||
.setBranch(branch.getName()) | |||
.setTitle(branch.getName()) | |||
.build(); | |||
dto.setPullRequestData(pullRequestData); | |||
} else { | |||
dto.setKey(branch.getName()); | |||
} | |||
return dto; | |||
} | |||
@@ -85,6 +85,11 @@ public class DefaultBranchImpl implements Branch { | |||
return !isLegacyBranch; | |||
} | |||
@Override | |||
public String getPullRequestId() { | |||
throw new IllegalStateException("Only a branch of type PULL_REQUEST can have a pull request id."); | |||
} | |||
@Override | |||
public String generateKey(ScannerReport.Component module, @Nullable ScannerReport.Component fileOrDir) { | |||
String moduleWithBranch = module.getKey(); |
@@ -31,7 +31,7 @@ import org.sonar.db.component.ComponentDto; | |||
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder; | |||
import static com.google.common.base.Preconditions.checkState; | |||
import static org.sonar.db.component.ComponentDto.removeBranchFromKey; | |||
import static org.sonar.db.component.ComponentDto.removeBranchAndPullRequestFromKey; | |||
/** | |||
* Cache a map between component keys and uuids in the merge branch | |||
@@ -74,7 +74,7 @@ public class MergeBranchComponentUuids { | |||
@CheckForNull | |||
public String getUuid(String dbKey) { | |||
lazyInit(); | |||
String cleanComponentKey = removeBranchFromKey(dbKey); | |||
String cleanComponentKey = removeBranchAndPullRequestFromKey(dbKey); | |||
return uuidsByKey.get(cleanComponentKey); | |||
} | |||
} |
@@ -29,7 +29,7 @@ import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.component.KeyWithUuidDto; | |||
import static org.sonar.db.component.ComponentDto.removeBranchFromKey; | |||
import static org.sonar.db.component.ComponentDto.removeBranchAndPullRequestFromKey; | |||
/** | |||
* Cache a map of component key -> uuid in short branches that have issues with status either RESOLVED or CONFIRMED. | |||
@@ -51,7 +51,7 @@ public class ShortBranchComponentsWithIssues { | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
List<KeyWithUuidDto> components = dbClient.componentDao().selectComponentKeysHavingIssuesToMerge(dbSession, uuid); | |||
for (KeyWithUuidDto dto : components) { | |||
uuidsByKey.computeIfAbsent(removeBranchFromKey(dto.key()), s -> new HashSet<>()).add(dto.uuid()); | |||
uuidsByKey.computeIfAbsent(removeBranchAndPullRequestFromKey(dto.key()), s -> new HashSet<>()).add(dto.uuid()); | |||
} | |||
} | |||
} |
@@ -44,7 +44,7 @@ public class IssueTrackingDelegator { | |||
} | |||
public TrackingResult track(Component component) { | |||
if (analysisMetadataHolder.isShortLivingBranch()) { | |||
if (analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) { | |||
return standardResult(shortBranchTracker.track(component)); | |||
} else if (isFirstAnalysisSecondaryLongLivingBranch()) { | |||
Tracking<DefaultIssue, DefaultIssue> tracking = mergeBranchTracker.track(component); |
@@ -50,7 +50,7 @@ public class ShortBranchIssuesLoader { | |||
} | |||
public Collection<ShortBranchIssue> loadCandidateIssuesForMergingInTargetBranch(Component component) { | |||
String componentKey = ComponentDto.removeBranchFromKey(component.getKey()); | |||
String componentKey = ComponentDto.removeBranchAndPullRequestFromKey(component.getKey()); | |||
Set<String> uuids = shortBranchComponentsWithIssues.getUuids(componentKey); | |||
if (uuids.isEmpty()) { | |||
return Collections.emptyList(); |
@@ -67,7 +67,7 @@ public class LoadQualityGateStep implements ComputationStep { | |||
} | |||
private Optional<QualityGate> getShortLivingBranchQualityGate() { | |||
if (analysisMetadataHolder.isShortLivingBranch()) { | |||
if (analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) { | |||
Optional<QualityGate> qualityGate = qualityGateService.findById(ShortLivingBranchQualityGate.ID); | |||
if (qualityGate.isPresent()) { | |||
return qualityGate; |
@@ -69,8 +69,8 @@ public class QualityGateEventsStep implements ComputationStep { | |||
@Override | |||
public void execute() { | |||
// no notification on short living branch as there is no real Quality Gate on those | |||
if (analysisMetadataHolder.isShortLivingBranch()) { | |||
// no notification on short living branch and pull request as there is no real Quality Gate on those | |||
if (analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) { | |||
return; | |||
} | |||
new DepthTraversalTypeAwareCrawler( |
@@ -28,6 +28,7 @@ import java.util.Map; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.function.Predicate; | |||
import javax.annotation.CheckForNull; | |||
import org.sonar.api.issue.Issue; | |||
import org.sonar.api.utils.Duration; | |||
import org.sonar.core.issue.DefaultIssue; | |||
@@ -49,6 +50,7 @@ import org.sonar.server.issue.notification.NewIssuesNotificationFactory; | |||
import org.sonar.server.issue.notification.NewIssuesStatistics; | |||
import org.sonar.server.notification.NotificationService; | |||
import static org.sonar.db.component.BranchType.PULL_REQUEST; | |||
import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER; | |||
/** | |||
@@ -127,7 +129,7 @@ public class SendIssueNotificationsStep implements ComputationStep { | |||
IssueChangeNotification changeNotification = new IssueChangeNotification(); | |||
changeNotification.setRuleName(rules.getByKey(issue.ruleKey()).getName()); | |||
changeNotification.setIssue(issue); | |||
changeNotification.setProject(project.getPublicKey(), project.getName(), getBranchName()); | |||
changeNotification.setProject(project.getPublicKey(), project.getName(), getBranchName(), getPullRequest()); | |||
getComponentKey(issue).ifPresent(c -> changeNotification.setComponent(c.getPublicKey(), c.getName())); | |||
service.deliver(changeNotification); | |||
} | |||
@@ -136,7 +138,7 @@ public class SendIssueNotificationsStep implements ComputationStep { | |||
NewIssuesStatistics.Stats globalStatistics = statistics.globalStatistics(); | |||
NewIssuesNotification notification = newIssuesNotificationFactory | |||
.newNewIssuesNotication() | |||
.setProject(project.getPublicKey(), project.getName(), getBranchName()) | |||
.setProject(project.getPublicKey(), project.getName(), getBranchName(), getPullRequest()) | |||
.setProjectVersion(project.getReportAttributes().getVersion()) | |||
.setAnalysisDate(new Date(analysisDate)) | |||
.setStatistics(project.getName(), globalStatistics) | |||
@@ -155,7 +157,7 @@ public class SendIssueNotificationsStep implements ComputationStep { | |||
.newMyNewIssuesNotification() | |||
.setAssignee(assignee); | |||
myNewIssuesNotification | |||
.setProject(project.getPublicKey(), project.getName(), getBranchName()) | |||
.setProject(project.getPublicKey(), project.getName(), getBranchName(), getPullRequest()) | |||
.setProjectVersion(project.getReportAttributes().getVersion()) | |||
.setAnalysisDate(new Date(analysisDate)) | |||
.setStatistics(project.getName(), assigneeStatistics) | |||
@@ -185,9 +187,16 @@ public class SendIssueNotificationsStep implements ComputationStep { | |||
return "Send issue notifications"; | |||
} | |||
@CheckForNull | |||
private String getBranchName() { | |||
Branch branch = analysisMetadataHolder.getBranch(); | |||
return branch.isMain() ? null : branch.getName(); | |||
return branch.isMain() || branch.getType() == PULL_REQUEST ? null : branch.getName(); | |||
} | |||
@CheckForNull | |||
private String getPullRequest() { | |||
Branch branch = analysisMetadataHolder.getBranch(); | |||
return branch.getType() == PULL_REQUEST ? analysisMetadataHolder.getPullRequestId() : null; | |||
} | |||
} |
@@ -52,7 +52,7 @@ public class DuplicationsParser { | |||
this.componentDao = componentDao; | |||
} | |||
public List<Block> parse(DbSession session, ComponentDto component, @Nullable String branch, @Nullable String duplicationsData) { | |||
public List<Block> parse(DbSession session, ComponentDto component, @Nullable String branch, @Nullable String pullRequest, @Nullable String duplicationsData) { | |||
Map<String, ComponentDto> componentsByKey = newHashMap(); | |||
List<Block> blocks = newArrayList(); | |||
if (duplicationsData != null) { | |||
@@ -69,7 +69,7 @@ public class DuplicationsParser { | |||
String size = bCursor.getAttrValue("l"); | |||
String componentKey = bCursor.getAttrValue("r"); | |||
if (from != null && size != null && componentKey != null) { | |||
duplications.add(createDuplication(componentsByKey, branch, from, size, componentKey, session)); | |||
duplications.add(createDuplication(componentsByKey, branch, pullRequest, from, size, componentKey, session)); | |||
} | |||
} | |||
Collections.sort(duplications, new DuplicationComparator(component.uuid(), component.projectUuid())); | |||
@@ -83,12 +83,19 @@ public class DuplicationsParser { | |||
return blocks; | |||
} | |||
private Duplication createDuplication(Map<String, ComponentDto> componentsByKey, @Nullable String branch, String from, String size, String componentDbKey, DbSession session) { | |||
private Duplication createDuplication(Map<String, ComponentDto> componentsByKey, @Nullable String branch, @Nullable String pullRequest, String from, String size, | |||
String componentDbKey, DbSession session) { | |||
String componentKey = convertToKey(componentDbKey); | |||
ComponentDto component = componentsByKey.get(componentKey); | |||
if (component == null) { | |||
Optional<ComponentDto> componentDtoOptional = branch == null ? componentDao.selectByKey(session, componentKey) | |||
: Optional.fromNullable(componentDao.selectByKeyAndBranch(session, componentKey, branch).orElseGet(null)); | |||
Optional<ComponentDto> componentDtoOptional; | |||
if (branch != null) { | |||
componentDtoOptional = Optional.fromNullable(componentDao.selectByKeyAndBranch(session, componentKey, branch).orElseGet(null)); | |||
} else if (pullRequest != null) { | |||
componentDtoOptional = Optional.fromNullable(componentDao.selectByKeyAndPullRequest(session, componentKey, pullRequest).orElseGet(null)); | |||
} else { | |||
componentDtoOptional = componentDao.selectByKey(session, componentKey); | |||
} | |||
component = componentDtoOptional.isPresent() ? componentDtoOptional.get() : null; | |||
componentsByKey.put(componentKey, component); | |||
} |
@@ -37,11 +37,15 @@ import org.sonar.server.user.UserSession; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static org.sonar.server.component.ComponentFinder.ParamNames.UUID_AND_KEY; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH; | |||
public class ShowAction implements DuplicationsWsAction { | |||
private static final String PARAM_KEY = "key"; | |||
private static final String PARAM_UUID = "uuid"; | |||
private static final String PARAM_BRANCH = "branch"; | |||
private static final String PARAM_PULL_REQUEST = "pullRequest"; | |||
private final DbClient dbClient; | |||
private final DuplicationsParser parser; | |||
private final ShowResponseBuilder responseBuilder; | |||
@@ -68,21 +72,28 @@ public class ShowAction implements DuplicationsWsAction { | |||
new Change("6.5", "The fields 'uuid', 'projectUuid', 'subProjectUuid' are deprecated in the response.")); | |||
action | |||
.createParam("key") | |||
.createParam(PARAM_KEY) | |||
.setDescription("File key") | |||
.setExampleValue("my_project:/src/foo/Bar.php"); | |||
action | |||
.createParam("uuid") | |||
.createParam(PARAM_UUID) | |||
.setDeprecatedSince("6.5") | |||
.setDescription("File ID. If provided, 'key' must not be provided.") | |||
.setExampleValue("584a89f2-8037-4f7b-b82c-8b45d2d63fb2"); | |||
action | |||
.createParam("branch") | |||
.createParam(PARAM_BRANCH) | |||
.setDescription("Branch key") | |||
.setInternal(true) | |||
.setExampleValue(KEY_BRANCH_EXAMPLE_001); | |||
action | |||
.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setInternal(true) | |||
.setSince("7.1") | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001); | |||
} | |||
@Override | |||
@@ -92,19 +103,22 @@ public class ShowAction implements DuplicationsWsAction { | |||
userSession.checkComponentPermission(UserRole.CODEVIEWER, component); | |||
String duplications = findDataFromComponent(dbSession, component); | |||
String branch = component.getBranch(); | |||
List<DuplicationsParser.Block> blocks = parser.parse(dbSession, component, branch, duplications); | |||
writeProtobuf(responseBuilder.build(dbSession, blocks, branch), request, response); | |||
String pullRequest = component.getPullRequest(); | |||
List<DuplicationsParser.Block> blocks = parser.parse(dbSession, component, branch, pullRequest, duplications); | |||
writeProtobuf(responseBuilder.build(dbSession, blocks, branch, pullRequest), request, response); | |||
} | |||
} | |||
private ComponentDto loadComponent(DbSession dbSession, Request request) { | |||
String componentUuid = request.param("uuid"); | |||
String branch = request.param("branch"); | |||
checkArgument(componentUuid == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", "uuid", PARAM_BRANCH); | |||
if (branch == null) { | |||
return componentFinder.getByUuidOrKey(dbSession, componentUuid, request.param("key"), UUID_AND_KEY); | |||
String componentUuid = request.param(PARAM_UUID); | |||
String branch = request.param(PARAM_BRANCH); | |||
String pullRequest = request.param(PARAM_PULL_REQUEST); | |||
checkArgument(componentUuid == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'", PARAM_UUID, | |||
PARAM_BRANCH, PARAM_PULL_REQUEST); | |||
if (branch == null && pullRequest == null) { | |||
return componentFinder.getByUuidOrKey(dbSession, componentUuid, request.param(PARAM_KEY), UUID_AND_KEY); | |||
} | |||
return componentFinder.getByKeyAndOptionalBranch(dbSession, request.mandatoryParam("key"), branch); | |||
return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, request.mandatoryParam(PARAM_KEY), branch, pullRequest); | |||
} | |||
@CheckForNull |
@@ -49,14 +49,14 @@ public class ShowResponseBuilder { | |||
this.componentDao = componentDao; | |||
} | |||
ShowResponse build(DbSession session, List<DuplicationsParser.Block> blocks, @Nullable String branch) { | |||
ShowResponse build(DbSession session, List<DuplicationsParser.Block> blocks, @Nullable String branch, @Nullable String pullRequest) { | |||
ShowResponse.Builder response = ShowResponse.newBuilder(); | |||
Map<String, String> refByComponentKey = newHashMap(); | |||
blocks.stream() | |||
.map(block -> toWsDuplication(block, refByComponentKey)) | |||
.forEach(response::addDuplications); | |||
writeFiles(session, response, refByComponentKey, branch); | |||
writeFiles(session, response, refByComponentKey, branch, pullRequest); | |||
return response.build(); | |||
} | |||
@@ -86,7 +86,8 @@ public class ShowResponseBuilder { | |||
return block; | |||
} | |||
private static Duplications.File toWsFile(ComponentDto file, @Nullable ComponentDto project, @Nullable ComponentDto subProject, @Nullable String branch) { | |||
private static Duplications.File toWsFile(ComponentDto file, @Nullable ComponentDto project, @Nullable ComponentDto subProject, @Nullable String branch, | |||
@Nullable String pullRequest) { | |||
Duplications.File.Builder wsFile = Duplications.File.newBuilder(); | |||
wsFile.setKey(file.getKey()); | |||
wsFile.setUuid(file.uuid()); | |||
@@ -102,15 +103,14 @@ public class ShowResponseBuilder { | |||
wsFile.setSubProjectUuid(subProject.uuid()); | |||
wsFile.setSubProjectName(subProject.longName()); | |||
} | |||
if (branch != null) { | |||
wsFile.setBranch(branch); | |||
} | |||
setNullable(branch, wsFile::setBranch); | |||
setNullable(pullRequest, wsFile::setPullRequest); | |||
return wsFile; | |||
}); | |||
return wsFile.build(); | |||
} | |||
private void writeFiles(DbSession session, ShowResponse.Builder response, Map<String, String> refByComponentKey, @Nullable String branch) { | |||
private void writeFiles(DbSession session, ShowResponse.Builder response, Map<String, String> refByComponentKey, @Nullable String branch, @Nullable String pullRequest) { | |||
Map<String, ComponentDto> projectsByUuid = newHashMap(); | |||
Map<String, ComponentDto> parentModulesByUuid = newHashMap(); | |||
Map<String, Duplications.File> filesByRef = response.getMutableFiles(); | |||
@@ -124,7 +124,7 @@ public class ShowResponseBuilder { | |||
ComponentDto project = getProject(file.projectUuid(), projectsByUuid, session); | |||
ComponentDto parentModule = getParentProject(file.getRootUuid(), parentModulesByUuid, session); | |||
filesByRef.put(ref, toWsFile(file, project, parentModule, branch)); | |||
filesByRef.put(ref, toWsFile(file, project, parentModule, branch, pullRequest)); | |||
} | |||
} | |||
} |
@@ -198,6 +198,7 @@ public class IssueQueryFactory { | |||
Collection<String> componentRootUuids = request.getComponentRootUuids(); | |||
Collection<String> componentRoots = request.getComponentRoots(); | |||
String branch = request.getBranch(); | |||
String pullRequest = request.getPullRequest(); | |||
boolean effectiveOnComponentOnly = false; | |||
@@ -208,15 +209,15 @@ public class IssueQueryFactory { | |||
if (componentRootUuids != null) { | |||
allComponents.addAll(getComponentsFromUuids(session, componentRootUuids)); | |||
} else if (componentRoots != null) { | |||
allComponents.addAll(getComponentsFromKeys(session, componentRoots, branch)); | |||
allComponents.addAll(getComponentsFromKeys(session, componentRoots, branch, pullRequest)); | |||
} else if (components != null) { | |||
allComponents.addAll(getComponentsFromKeys(session, components, branch)); | |||
allComponents.addAll(getComponentsFromKeys(session, components, branch, pullRequest)); | |||
effectiveOnComponentOnly = true; | |||
} else if (componentUuids != null) { | |||
allComponents.addAll(getComponentsFromUuids(session, componentUuids)); | |||
effectiveOnComponentOnly = BooleanUtils.isTrue(onComponentOnly); | |||
} else if (componentKeys != null) { | |||
allComponents.addAll(getComponentsFromKeys(session, componentKeys, branch)); | |||
allComponents.addAll(getComponentsFromKeys(session, componentKeys, branch, pullRequest)); | |||
effectiveOnComponentOnly = BooleanUtils.isTrue(onComponentOnly); | |||
} | |||
@@ -229,13 +230,11 @@ public class IssueQueryFactory { | |||
.count() <= 1; | |||
} | |||
private void addComponentParameters(IssueQuery.Builder builder, DbSession session, boolean onComponentOnly, | |||
List<ComponentDto> components, SearchRequest request) { | |||
private void addComponentParameters(IssueQuery.Builder builder, DbSession session, boolean onComponentOnly, List<ComponentDto> components, SearchRequest request) { | |||
builder.onComponentOnly(onComponentOnly); | |||
if (onComponentOnly) { | |||
builder.componentUuids(components.stream().map(ComponentDto::uuid).collect(toList())); | |||
setBranch(builder, components.get(0), request.getBranch()); | |||
setBranch(builder, components.get(0), request.getBranch(), request.getPullRequest()); | |||
return; | |||
} | |||
@@ -246,9 +245,9 @@ public class IssueQueryFactory { | |||
if (projectUuids != null) { | |||
builder.projectUuids(projectUuids); | |||
} else if (projectKeys != null) { | |||
List<ComponentDto> projects = getComponentsFromKeys(session, projectKeys, request.getBranch()); | |||
List<ComponentDto> projects = getComponentsFromKeys(session, projectKeys, request.getBranch(), request.getPullRequest()); | |||
builder.projectUuids(projects.stream().map(IssueQueryFactory::toProjectUuid).collect(toList())); | |||
setBranch(builder, projects.get(0), request.getBranch()); | |||
setBranch(builder, projects.get(0), request.getBranch(), request.getPullRequest()); | |||
} | |||
builder.moduleUuids(request.getModuleUuids()); | |||
builder.directories(request.getDirectories()); | |||
@@ -269,7 +268,7 @@ public class IssueQueryFactory { | |||
Set<String> qualifiers = components.stream().map(ComponentDto::qualifier).collect(toHashSet()); | |||
checkArgument(qualifiers.size() == 1, "All components must have the same qualifier, found %s", String.join(",", qualifiers)); | |||
setBranch(builder, components.get(0), request.getBranch()); | |||
setBranch(builder, components.get(0), request.getBranch(), request.getPullRequest()); | |||
String qualifier = qualifiers.iterator().next(); | |||
switch (qualifier) { | |||
case Qualifiers.VIEW: | |||
@@ -345,10 +344,15 @@ public class IssueQueryFactory { | |||
builder.directories(directoryPaths); | |||
} | |||
private List<ComponentDto> getComponentsFromKeys(DbSession dbSession, Collection<String> componentKeys, @Nullable String branch) { | |||
List<ComponentDto> componentDtos = branch == null | |||
? dbClient.componentDao().selectByKeys(dbSession, componentKeys) | |||
: dbClient.componentDao().selectByKeysAndBranch(dbSession, componentKeys, branch); | |||
private List<ComponentDto> getComponentsFromKeys(DbSession dbSession, Collection<String> componentKeys, @Nullable String branch, @Nullable String pullRequest) { | |||
List<ComponentDto> componentDtos; | |||
if (branch != null) { | |||
componentDtos = dbClient.componentDao().selectByKeysAndBranch(dbSession, componentKeys, branch); | |||
} else if (pullRequest != null) { | |||
componentDtos = dbClient.componentDao().selectByKeysAndPullRequest(dbSession, componentKeys, pullRequest); | |||
} else { | |||
componentDtos = dbClient.componentDao().selectByKeys(dbSession, componentKeys); | |||
} | |||
if (!componentKeys.isEmpty() && componentDtos.isEmpty()) { | |||
return singletonList(UNKNOWN_COMPONENT); | |||
} | |||
@@ -376,8 +380,11 @@ public class IssueQueryFactory { | |||
return mainBranchProjectUuid == null ? componentDto.projectUuid() : mainBranchProjectUuid; | |||
} | |||
private static void setBranch(IssueQuery.Builder builder, ComponentDto component, @Nullable String branch) { | |||
builder.branchUuid(branch == null ? null : component.projectUuid()); | |||
builder.mainBranch(branch == null || component.equals(UNKNOWN_COMPONENT) || !branch.equals(component.getBranch())); | |||
private static void setBranch(IssueQuery.Builder builder, ComponentDto component, @Nullable String branch, @Nullable String pullRequest) { | |||
builder.branchUuid(branch == null && pullRequest == null ? null : component.projectUuid()); | |||
builder.mainBranch(UNKNOWN_COMPONENT.equals(component) | |||
|| (branch == null && pullRequest == null) | |||
|| (branch != null && !branch.equals(component.getBranch())) | |||
|| (pullRequest != null && !pullRequest.equals(component.getPullRequest()))); | |||
} | |||
} |
@@ -48,6 +48,7 @@ public class SearchRequest { | |||
private List<String> moduleUuids; | |||
private Boolean onComponentOnly; | |||
private String branch; | |||
private String pullRequest; | |||
private String organization; | |||
private Integer page; | |||
private Integer pageSize; | |||
@@ -453,4 +454,14 @@ public class SearchRequest { | |||
this.branch = branch; | |||
return this; | |||
} | |||
@CheckForNull | |||
public String getPullRequest() { | |||
return pullRequest; | |||
} | |||
public SearchRequest setPullRequest(@Nullable String pullRequest) { | |||
this.pullRequest = pullRequest; | |||
return this; | |||
} | |||
} |
@@ -54,6 +54,7 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate { | |||
static final String FIELD_PROJECT_VERSION = "projectVersion"; | |||
static final String FIELD_ASSIGNEE = "assignee"; | |||
static final String FIELD_BRANCH = "branch"; | |||
static final String FIELD_PULL_REQUEST = "pullRequest"; | |||
protected final EmailSettings settings; | |||
protected final I18n i18n; | |||
@@ -78,12 +79,16 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate { | |||
} | |||
String projectName = checkNotNull(notification.getFieldValue(FIELD_PROJECT_NAME)); | |||
String branchName = notification.getFieldValue(FIELD_BRANCH); | |||
String pullRequest = notification.getFieldValue(FIELD_PULL_REQUEST); | |||
StringBuilder message = new StringBuilder(); | |||
message.append("Project: ").append(projectName).append(NEW_LINE); | |||
if (branchName != null) { | |||
message.append("Branch: ").append(branchName).append(NEW_LINE); | |||
} | |||
if (pullRequest!= null) { | |||
message.append("Pull request: ").append(pullRequest).append(NEW_LINE); | |||
} | |||
String version = notification.getFieldValue(FIELD_PROJECT_VERSION); | |||
if (version != null) { | |||
message.append("Version: ").append(version).append(NEW_LINE); | |||
@@ -203,6 +208,10 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate { | |||
if (branchName != null) { | |||
url += "&branch=" + encode(branchName); | |||
} | |||
String pullRequest = notification.getFieldValue(FIELD_PULL_REQUEST); | |||
if (pullRequest != null) { | |||
url += "&pullRequest=" + encode(pullRequest); | |||
} | |||
url += "&createdAt=" + encode(DateUtils.formatDateTime(date)); | |||
message | |||
.append("More details at: ") |
@@ -29,6 +29,11 @@ import org.sonar.core.issue.DefaultIssue; | |||
import org.sonar.core.issue.FieldDiffs; | |||
import org.sonar.db.component.ComponentDto; | |||
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_BRANCH; | |||
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PROJECT_KEY; | |||
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PROJECT_NAME; | |||
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PULL_REQUEST; | |||
public class IssueChangeNotification extends Notification { | |||
public static final String TYPE = "issue-changes"; | |||
@@ -54,14 +59,17 @@ public class IssueChangeNotification extends Notification { | |||
} | |||
public IssueChangeNotification setProject(ComponentDto project) { | |||
return setProject(project.getKey(), project.name(), project.getBranch()); | |||
return setProject(project.getKey(), project.name(), project.getBranch(), project.getPullRequest()); | |||
} | |||
public IssueChangeNotification setProject(String projectKey, String projectName, @Nullable String branch) { | |||
setFieldValue("projectName", projectName); | |||
setFieldValue("projectKey", projectKey); | |||
public IssueChangeNotification setProject(String projectKey, String projectName, @Nullable String branch, @Nullable String pullRequest) { | |||
setFieldValue(FIELD_PROJECT_NAME, projectName); | |||
setFieldValue(FIELD_PROJECT_KEY, projectKey); | |||
if (branch != null) { | |||
setFieldValue("branch", branch); | |||
setFieldValue(FIELD_BRANCH, branch); | |||
} | |||
if (pullRequest != null) { | |||
setFieldValue(FIELD_PULL_REQUEST, pullRequest); | |||
} | |||
return this; | |||
} |
@@ -33,6 +33,8 @@ import org.sonar.plugins.emailnotifications.api.EmailMessage; | |||
import org.sonar.plugins.emailnotifications.api.EmailTemplate; | |||
import static java.net.URLEncoder.encode; | |||
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_BRANCH; | |||
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PULL_REQUEST; | |||
/** | |||
* Creates email message for notification "issue-changes". | |||
@@ -99,10 +101,14 @@ public class IssueChangesEmailTemplate extends EmailTemplate { | |||
private static void appendHeader(Notification notif, StringBuilder sb) { | |||
appendLine(sb, StringUtils.defaultString(notif.getFieldValue("componentName"), notif.getFieldValue("componentKey"))); | |||
String branchName = notif.getFieldValue("branch"); | |||
String branchName = notif.getFieldValue(FIELD_BRANCH); | |||
if (branchName != null) { | |||
appendField(sb, "Branch", null, branchName); | |||
} | |||
String pullRequest = notif.getFieldValue(FIELD_PULL_REQUEST); | |||
if (pullRequest != null) { | |||
appendField(sb, "Pull request", null, pullRequest); | |||
} | |||
appendField(sb, "Rule", null, notif.getFieldValue("ruleName")); | |||
appendField(sb, "Message", null, notif.getFieldValue("message")); | |||
} | |||
@@ -114,10 +120,14 @@ public class IssueChangesEmailTemplate extends EmailTemplate { | |||
.append("/project/issues?id=").append(encode(notification.getFieldValue("projectKey"), "UTF-8")) | |||
.append("&issues=").append(issueKey) | |||
.append("&open=").append(issueKey); | |||
String branchName = notification.getFieldValue("branch"); | |||
String branchName = notification.getFieldValue(FIELD_BRANCH); | |||
if (branchName != null) { | |||
sb.append("&branch=").append(branchName); | |||
} | |||
String pullRequest = notification.getFieldValue(FIELD_PULL_REQUEST); | |||
if (pullRequest != null) { | |||
sb.append("&pullRequest=").append(pullRequest); | |||
} | |||
sb.append(NEW_LINE); | |||
} catch (UnsupportedEncodingException e) { | |||
throw new IllegalStateException("Encoding not supported", e); |
@@ -65,10 +65,14 @@ public class MyNewIssuesEmailTemplate extends AbstractNewIssuesEmailTemplate { | |||
settings.getServerBaseURL(), | |||
encode(projectKey), | |||
encode(assignee)); | |||
String branchName = notification.getFieldValue("branch"); | |||
String branchName = notification.getFieldValue(FIELD_BRANCH); | |||
if (branchName != null) { | |||
url += "&branch=" + encode(branchName); | |||
} | |||
String pullRequest = notification.getFieldValue(FIELD_PULL_REQUEST); | |||
if (pullRequest != null) { | |||
url += "&pullRequest=" + encode(pullRequest); | |||
} | |||
url += "&createdAt=" + encode(DateUtils.formatDateTime(date)); | |||
message | |||
.append("More details at: ") |
@@ -46,6 +46,7 @@ import org.sonar.server.user.index.UserIndex; | |||
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_BRANCH; | |||
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PROJECT_VERSION; | |||
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PULL_REQUEST; | |||
import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_DATE; | |||
import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_KEY; | |||
import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_NAME; | |||
@@ -79,12 +80,15 @@ public class NewIssuesNotification extends Notification { | |||
return this; | |||
} | |||
public NewIssuesNotification setProject(String projectKey, String projectName, @Nullable String branchName) { | |||
public NewIssuesNotification setProject(String projectKey, String projectName, @Nullable String branchName, @Nullable String pullRequest) { | |||
setFieldValue(FIELD_PROJECT_NAME, projectName); | |||
setFieldValue(FIELD_PROJECT_KEY, projectKey); | |||
if (branchName != null) { | |||
setFieldValue(FIELD_BRANCH, branchName); | |||
} | |||
if (pullRequest != null) { | |||
setFieldValue(FIELD_PULL_REQUEST, pullRequest); | |||
} | |||
return this; | |||
} | |||
@@ -72,6 +72,7 @@ import static org.sonar.core.util.stream.MoreCollectors.toSet; | |||
import static org.sonar.server.es.SearchOptions.MAX_LIMIT; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_SEARCH; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.DEPRECATED_FACET_MODE_DEBT; | |||
@@ -106,6 +107,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PLANNED; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECTS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECT_KEYS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECT_UUIDS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PULL_REQUEST; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_REPORTERS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLUTIONS; | |||
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLVED; | |||
@@ -319,6 +321,12 @@ public class SearchAction implements IssuesWsAction { | |||
.setInternal(true) | |||
.setSince("6.6"); | |||
action.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001) | |||
.setInternal(true) | |||
.setSince("7.1"); | |||
action.createParam(PARAM_ORGANIZATION) | |||
.setDescription("Organization key") | |||
.setRequired(false) | |||
@@ -568,6 +576,7 @@ public class SearchAction implements IssuesWsAction { | |||
.setModuleUuids(request.paramAsStrings(PARAM_MODULE_UUIDS)) | |||
.setOnComponentOnly(request.paramAsBoolean(PARAM_ON_COMPONENT_ONLY)) | |||
.setBranch(request.param(PARAM_BRANCH)) | |||
.setPullRequest(request.param(PARAM_PULL_REQUEST)) | |||
.setOrganization(request.param(PARAM_ORGANIZATION)) | |||
.setPage(request.mandatoryParamAsInt(Param.PAGE)) | |||
.setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)) |
@@ -162,6 +162,7 @@ public class SearchResponseFormat { | |||
issueBuilder.setOrganization(data.getOrganizationKey(component.getOrganizationUuid())); | |||
issueBuilder.setComponent(component.getKey()); | |||
setNullable(component.getBranch(), issueBuilder::setBranch); | |||
setNullable(component.getPullRequest(), issueBuilder::setPullRequest); | |||
ComponentDto project = data.getComponentByUuid(dto.getProjectUuid()); | |||
if (project != null) { | |||
issueBuilder.setProject(project.getKey()); | |||
@@ -309,6 +310,7 @@ public class SearchResponseFormat { | |||
.setLongName(nullToEmpty(dto.longName())) | |||
.setEnabled(dto.isEnabled()); | |||
setNullable(dto.getBranch(), builder::setBranch); | |||
setNullable(dto.getPullRequest(), builder::setPullRequest); | |||
String path = dto.path(); | |||
// path is not applicable to the components that are not files. | |||
// Value must not be "" in this case. |
@@ -63,7 +63,7 @@ public class LiveQualityGateComputerImpl implements LiveQualityGateComputer { | |||
@Override | |||
public QualityGate loadQualityGate(DbSession dbSession, OrganizationDto organization, ComponentDto project, BranchDto branch) { | |||
if (branch.getBranchType() == BranchType.SHORT) { | |||
if (branch.getBranchType() == BranchType.SHORT || branch.getBranchType() == BranchType.PULL_REQUEST) { | |||
return ShortLivingBranchQualityGate.GATE; | |||
} | |||
@@ -58,17 +58,7 @@ import static java.util.Collections.emptyMap; | |||
import static java.util.Collections.singletonList; | |||
import static java.util.Collections.singletonMap; | |||
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01; | |||
import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT; | |||
import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter; | |||
import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric; | |||
import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.checkRequest; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_KEY; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.ACTION_COMPONENT; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_METRICS; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_PERIODS; | |||
@@ -80,6 +70,18 @@ import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_COMPONENT | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_DEVELOPER_ID; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_DEVELOPER_KEY; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_KEYS; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST; | |||
import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter; | |||
import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric; | |||
import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.checkRequest; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
public class ComponentAction implements MeasuresWsAction { | |||
private static final Set<String> QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE = ImmutableSortedSet.of(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE); | |||
@@ -123,6 +125,12 @@ public class ComponentAction implements MeasuresWsAction { | |||
.setInternal(true) | |||
.setSince("6.6"); | |||
action.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001) | |||
.setInternal(true) | |||
.setSince("7.1"); | |||
createMetricKeysParameter(action); | |||
createAdditionalFieldsParameter(action); | |||
createDeveloperParameters(action); | |||
@@ -156,10 +164,14 @@ public class ComponentAction implements MeasuresWsAction { | |||
String componentKey = request.getComponent(); | |||
String componentId = request.getComponentId(); | |||
String branch = request.getBranch(); | |||
checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", DEPRECATED_PARAM_COMPONENT_ID, PARAM_BRANCH); | |||
return branch == null | |||
? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT) | |||
: componentFinder.getByKeyAndBranch(dbSession, componentKey, branch); | |||
String pullRequest = request.getPullRequest(); | |||
checkArgument(componentId == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'", | |||
DEPRECATED_PARAM_COMPONENT_ID, PARAM_BRANCH, PARAM_PULL_REQUEST); | |||
if (branch == null && pullRequest == null) { | |||
return componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_KEY); | |||
} | |||
checkRequest(componentKey != null, "The '%s' parameter is missing", PARAM_COMPONENT); | |||
return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest); | |||
} | |||
private Optional<ComponentDto> getReferenceComponent(DbSession dbSession, ComponentDto component) { | |||
@@ -251,6 +263,7 @@ public class ComponentAction implements MeasuresWsAction { | |||
.setComponentId(request.param(DEPRECATED_PARAM_COMPONENT_ID)) | |||
.setComponent(request.param(PARAM_COMPONENT)) | |||
.setBranch(request.param(PARAM_BRANCH)) | |||
.setPullRequest(request.param(PARAM_PULL_REQUEST)) | |||
.setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS)) | |||
.setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS)); | |||
checkRequest(!componentRequest.getMetricKeys().isEmpty(), "At least one metric key must be provided"); | |||
@@ -265,6 +278,7 @@ public class ComponentAction implements MeasuresWsAction { | |||
private String componentId; | |||
private String component; | |||
private String branch; | |||
private String pullRequest; | |||
private List<String> metricKeys; | |||
private List<String> additionalFields; | |||
private String developerId; | |||
@@ -308,6 +322,16 @@ public class ComponentAction implements MeasuresWsAction { | |||
return this; | |||
} | |||
@CheckForNull | |||
public String getPullRequest() { | |||
return pullRequest; | |||
} | |||
public ComponentRequest setPullRequest(@Nullable String pullRequest) { | |||
this.pullRequest = pullRequest; | |||
return this; | |||
} | |||
private List<String> getMetricKeys() { | |||
return metricKeys; | |||
} |
@@ -59,6 +59,7 @@ class ComponentDtoToWsComponent { | |||
.setName(component.name()) | |||
.setQualifier(component.qualifier()); | |||
Protobuf.setNullable(component.getBranch(), wsComponent::setBranch); | |||
Protobuf.setNullable(component.getPullRequest(), wsComponent::setPullRequest); | |||
Protobuf.setNullable(component.path(), wsComponent::setPath); | |||
Protobuf.setNullable(component.description(), wsComponent::setDescription); | |||
Protobuf.setNullable(component.language(), wsComponent::setLanguage); |
@@ -84,20 +84,6 @@ import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02; | |||
import static org.sonar.db.component.ComponentTreeQuery.Strategy.CHILDREN; | |||
import static org.sonar.db.component.ComponentTreeQuery.Strategy.LEAVES; | |||
import static org.sonar.server.component.ComponentFinder.ParamNames.BASE_COMPONENT_ID_AND_KEY; | |||
import static org.sonar.server.component.ComponentFinder.ParamNames.DEVELOPER_ID_AND_KEY; | |||
import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent; | |||
import static org.sonar.server.measure.ws.MeasureDtoToWsMeasure.updateMeasureBuilder; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter; | |||
import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric; | |||
import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter; | |||
import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext; | |||
import static org.sonar.server.ws.WsUtils.checkRequest; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.ACTION_COMPONENT_TREE; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_METRICS; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_PERIODS; | |||
@@ -112,8 +98,23 @@ import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_KE | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_PERIOD_SORT; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_SORT; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_SORT_FILTER; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_QUALIFIERS; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_STRATEGY; | |||
import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent; | |||
import static org.sonar.server.measure.ws.MeasureDtoToWsMeasure.updateMeasureBuilder; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters; | |||
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter; | |||
import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric; | |||
import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext; | |||
import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter; | |||
import static org.sonar.server.ws.WsUtils.checkRequest; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
/** | |||
* <p>Navigate through components based on different strategy with specified measures. | |||
@@ -216,6 +217,12 @@ public class ComponentTreeAction implements MeasuresWsAction { | |||
.setInternal(true) | |||
.setSince("6.6"); | |||
action.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001) | |||
.setInternal(true) | |||
.setSince("7.1"); | |||
action.createParam(PARAM_METRIC_SORT) | |||
.setDescription( | |||
format("Metric key to sort by. The '%s' parameter must contain the '%s' or '%s' value. It must be part of the '%s' parameter", Param.SORT, METRIC_SORT, METRIC_PERIOD_SORT, | |||
@@ -343,6 +350,7 @@ public class ComponentTreeAction implements MeasuresWsAction { | |||
.setBaseComponentId(request.param(DEPRECATED_PARAM_BASE_COMPONENT_ID)) | |||
.setComponent(request.param(PARAM_COMPONENT)) | |||
.setBranch(request.param(PARAM_BRANCH)) | |||
.setPullRequest(request.param(PARAM_PULL_REQUEST)) | |||
.setMetricKeys(metricKeys) | |||
.setStrategy(request.mandatoryParam(PARAM_STRATEGY)) | |||
.setQualifiers(request.paramAsStrings(PARAM_QUALIFIERS)) | |||
@@ -428,13 +436,17 @@ public class ComponentTreeAction implements MeasuresWsAction { | |||
} | |||
private ComponentDto loadComponent(DbSession dbSession, ComponentTreeRequest request) { | |||
String componentKey = request.getComponent(); | |||
String componentId = request.getBaseComponentId(); | |||
String componentKey = request.getComponent(); | |||
String branch = request.getBranch(); | |||
checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", DEPRECATED_PARAM_BASE_COMPONENT_ID, PARAM_BRANCH); | |||
return branch == null | |||
? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, BASE_COMPONENT_ID_AND_KEY) | |||
: componentFinder.getByKeyAndBranch(dbSession, componentKey, branch); | |||
String pullRequest = request.getPullRequest(); | |||
checkArgument(componentId == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'", | |||
DEPRECATED_PARAM_BASE_COMPONENT_ID, PARAM_BRANCH, PARAM_PULL_REQUEST); | |||
if (branch == null && pullRequest == null) { | |||
return componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, BASE_COMPONENT_ID_AND_KEY); | |||
} | |||
checkRequest(componentKey != null, "The '%s' parameter is missing", PARAM_COMPONENT); | |||
return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest); | |||
} | |||
private Map<String, ComponentDto> searchReferenceComponentsById(DbSession dbSession, List<ComponentDto> components) { |
@@ -28,6 +28,7 @@ class ComponentTreeRequest { | |||
private String baseComponentId; | |||
private String component; | |||
private String branch; | |||
private String pullRequest; | |||
private String strategy; | |||
private List<String> qualifiers; | |||
private List<String> additionalFields; | |||
@@ -81,6 +82,16 @@ class ComponentTreeRequest { | |||
return this; | |||
} | |||
@CheckForNull | |||
public String getPullRequest() { | |||
return pullRequest; | |||
} | |||
public ComponentTreeRequest setPullRequest(@Nullable String pullRequest) { | |||
this.pullRequest = pullRequest; | |||
return this; | |||
} | |||
@CheckForNull | |||
public String getStrategy() { | |||
return strategy; |
@@ -25,6 +25,8 @@ import java.util.List; | |||
import java.util.Set; | |||
import java.util.function.Function; | |||
import java.util.stream.Stream; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.server.ws.Request; | |||
import org.sonar.api.server.ws.Response; | |||
import org.sonar.api.server.ws.WebService; | |||
@@ -46,22 +48,21 @@ import org.sonar.server.user.UserSession; | |||
import org.sonar.server.ws.KeyExamples; | |||
import org.sonarqube.ws.Measures.SearchHistoryResponse; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import static java.lang.String.format; | |||
import static org.sonar.api.utils.DateUtils.parseEndingDateOrDateTime; | |||
import static org.sonar.api.utils.DateUtils.parseStartingDateOrDateTime; | |||
import static org.sonar.core.util.Protobuf.setNullable; | |||
import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.ACTION_SEARCH_HISTORY; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_COMPONENT; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_FROM; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRICS; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST; | |||
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_TO; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
public class SearchHistoryAction implements MeasuresWsAction { | |||
@@ -78,18 +79,6 @@ public class SearchHistoryAction implements MeasuresWsAction { | |||
this.userSession = userSession; | |||
} | |||
private static SearchHistoryRequest toWsRequest(Request request) { | |||
return SearchHistoryRequest.builder() | |||
.setComponent(request.mandatoryParam(PARAM_COMPONENT)) | |||
.setBranch(request.param(PARAM_BRANCH)) | |||
.setMetrics(request.mandatoryParamAsStrings(PARAM_METRICS)) | |||
.setFrom(request.param(PARAM_FROM)) | |||
.setTo(request.param(PARAM_TO)) | |||
.setPage(request.mandatoryParamAsInt(Param.PAGE)) | |||
.setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)) | |||
.build(); | |||
} | |||
@Override | |||
public void define(WebService.NewController context) { | |||
WebService.NewAction action = context.createAction(ACTION_SEARCH_HISTORY) | |||
@@ -112,6 +101,12 @@ public class SearchHistoryAction implements MeasuresWsAction { | |||
.setInternal(true) | |||
.setExampleValue(KEY_BRANCH_EXAMPLE_001); | |||
action.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setSince("7.1") | |||
.setInternal(true) | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001); | |||
action.createParam(PARAM_METRICS) | |||
.setDescription("Comma-separated list of metric keys") | |||
.setRequired(true) | |||
@@ -141,6 +136,19 @@ public class SearchHistoryAction implements MeasuresWsAction { | |||
writeProtobuf(searchHistoryResponse, request, response); | |||
} | |||
private static SearchHistoryRequest toWsRequest(Request request) { | |||
return SearchHistoryRequest.builder() | |||
.setComponent(request.mandatoryParam(PARAM_COMPONENT)) | |||
.setBranch(request.param(PARAM_BRANCH)) | |||
.setPullRequest(request.param(PARAM_PULL_REQUEST)) | |||
.setMetrics(request.mandatoryParamAsStrings(PARAM_METRICS)) | |||
.setFrom(request.param(PARAM_FROM)) | |||
.setTo(request.param(PARAM_TO)) | |||
.setPage(request.mandatoryParamAsInt(Param.PAGE)) | |||
.setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)) | |||
.build(); | |||
} | |||
private Function<SearchHistoryRequest, SearchHistoryResult> search() { | |||
return request -> { | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
@@ -199,15 +207,14 @@ public class SearchHistoryAction implements MeasuresWsAction { | |||
private ComponentDto loadComponent(DbSession dbSession, SearchHistoryRequest request) { | |||
String componentKey = request.getComponent(); | |||
String branch = request.getBranch(); | |||
if (branch != null) { | |||
return componentFinder.getByKeyAndBranch(dbSession, componentKey, branch); | |||
} | |||
return componentFinder.getByKey(dbSession, componentKey); | |||
String pullRequest = request.getPullRequest(); | |||
return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest); | |||
} | |||
static class SearchHistoryRequest { | |||
private final String component; | |||
private final String branch; | |||
private final String pullRequest; | |||
private final List<String> metrics; | |||
private final String from; | |||
private final String to; | |||
@@ -217,6 +224,7 @@ public class SearchHistoryAction implements MeasuresWsAction { | |||
public SearchHistoryRequest(Builder builder) { | |||
this.component = builder.component; | |||
this.branch = builder.branch; | |||
this.pullRequest = builder.pullRequest; | |||
this.metrics = builder.metrics; | |||
this.from = builder.from; | |||
this.to = builder.to; | |||
@@ -233,6 +241,11 @@ public class SearchHistoryAction implements MeasuresWsAction { | |||
return branch; | |||
} | |||
@CheckForNull | |||
public String getPullRequest() { | |||
return pullRequest; | |||
} | |||
public List<String> getMetrics() { | |||
return metrics; | |||
} | |||
@@ -263,6 +276,7 @@ public class SearchHistoryAction implements MeasuresWsAction { | |||
static class Builder { | |||
private String component; | |||
private String branch; | |||
private String pullRequest; | |||
private List<String> metrics; | |||
private String from; | |||
private String to; | |||
@@ -283,6 +297,11 @@ public class SearchHistoryAction implements MeasuresWsAction { | |||
return this; | |||
} | |||
public Builder setPullRequest(@Nullable String pullRequest) { | |||
this.pullRequest = pullRequest; | |||
return this; | |||
} | |||
public Builder setMetrics(List<String> metrics) { | |||
this.metrics = metrics; | |||
return this; |
@@ -38,6 +38,7 @@ import org.sonar.server.authentication.LogOAuthWarning; | |||
import org.sonar.server.badge.ws.ProjectBadgesWsModule; | |||
import org.sonar.server.batch.BatchWsModule; | |||
import org.sonar.server.branch.BranchFeatureProxyImpl; | |||
import org.sonar.server.branch.pr.ws.PullRequestWsModule; | |||
import org.sonar.server.branch.ws.BranchWsModule; | |||
import org.sonar.server.ce.ws.CeWsModule; | |||
import org.sonar.server.component.ComponentCleanerService; | |||
@@ -401,6 +402,7 @@ public class PlatformLevel4 extends PlatformLevel { | |||
// components | |||
BranchWsModule.class, | |||
PullRequestWsModule.class, | |||
ProjectsWsModule.class, | |||
ProjectsEsModule.class, | |||
ProjectTagsWsModule.class, |
@@ -28,6 +28,7 @@ public class ProjectAnalysesWsParameters { | |||
public static final String PARAM_FROM = "from"; | |||
public static final String PARAM_TO = "to"; | |||
public static final String PARAM_BRANCH = "branch"; | |||
public static final String PARAM_PULL_REQUEST = "pullRequest"; | |||
private ProjectAnalysesWsParameters() { | |||
// static access only |
@@ -47,15 +47,17 @@ import static org.sonar.api.utils.DateUtils.parseStartingDateOrDateTime; | |||
import static org.sonar.core.util.Protobuf.setNullable; | |||
import static org.sonar.db.component.SnapshotQuery.SORT_FIELD.BY_DATE; | |||
import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.DESC; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonar.server.projectanalysis.ws.EventCategory.OTHER; | |||
import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_BRANCH; | |||
import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_CATEGORY; | |||
import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_FROM; | |||
import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_PROJECT; | |||
import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_PULL_REQUEST; | |||
import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_TO; | |||
import static org.sonar.server.projectanalysis.ws.SearchRequest.DEFAULT_PAGE_SIZE; | |||
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; | |||
import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
public class SearchAction implements ProjectAnalysesWsAction { | |||
private static final Set<String> ALLOWED_QUALIFIERS = ImmutableSet.of(Qualifiers.PROJECT, Qualifiers.APP, Qualifiers.VIEW); | |||
@@ -92,6 +94,12 @@ public class SearchAction implements ProjectAnalysesWsAction { | |||
.setInternal(true) | |||
.setExampleValue(KEY_BRANCH_EXAMPLE_001); | |||
action.createParam(PARAM_PULL_REQUEST) | |||
.setDescription("Pull request id") | |||
.setSince("7.1") | |||
.setInternal(true) | |||
.setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001); | |||
action.createParam(PARAM_CATEGORY) | |||
.setDescription("Event category. Filter analyses that have at least one event of the category specified.") | |||
.setPossibleValues(EnumSet.allOf(EventCategory.class)) | |||
@@ -123,6 +131,7 @@ public class SearchAction implements ProjectAnalysesWsAction { | |||
return SearchRequest.builder() | |||
.setProject(request.mandatoryParam(PARAM_PROJECT)) | |||
.setBranch(request.param(PARAM_BRANCH)) | |||
.setPullRequest(request.param(PARAM_PULL_REQUEST)) | |||
.setCategory(category == null ? null : EventCategory.valueOf(category)) | |||
.setPage(request.mandatoryParamAsInt(Param.PAGE)) | |||
.setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE)) | |||
@@ -170,10 +179,8 @@ public class SearchAction implements ProjectAnalysesWsAction { | |||
private ComponentDto loadComponent(DbSession dbSession, SearchRequest request) { | |||
String project = request.getProject(); | |||
String branch = request.getBranch(); | |||
if (branch != null) { | |||
return componentFinder.getByKeyAndBranch(dbSession, project, branch); | |||
} | |||
return componentFinder.getByKey(dbSession, project); | |||
String pullRequest = request.getPullRequest(); | |||
return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, project, branch, pullRequest); | |||
} | |||
} |