diff --git a/README.md b/README.md index 31525a9..76f93b7 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,33 @@ It starts with the prefix `SCMM_CUSTOM_PROP_` and will end with the key of the c For example the custom property with the key `lang`, will be injected as the environment variable `SCMM_CUSTOM_PROP_lang`. The value of the environment variable, will be the value of the custom property. +#### Pull Request Labels + +For pull request builds, SCM-Manager pull request labels are injected as environment variables. +`SCMM_PR_LABELS` contains all labels as a JSON array, preserving the exact label values and order from SCM-Manager. +For simple access, the plugin also injects `SCMM_PR_LABEL_COUNT` and numbered variables `SCMM_PR_LABEL_1`, `SCMM_PR_LABEL_2`, and so on. + +Example: + +```groovy +pipeline { + agent any + + stages { + stage('Labels') { + steps { + script { + def labels = new groovy.json.JsonSlurperClassic().parseText(env.SCMM_PR_LABELS ?: '[]') + if (labels.contains('backend')) { + echo 'Backend label is set' + } + } + } + } + } +} +``` + ### Organization Folders – Namespaces If you want to have build jobs for every repository in a namespace, you can create "SCM-Manager Namespace" jobs. These will scan all repositories in the given namespace and create multibranch pipelines for each repository where a diff --git a/docs/de/index.md b/docs/de/index.md index fbf2ed5..0c83e39 100644 --- a/docs/de/index.md +++ b/docs/de/index.md @@ -102,6 +102,33 @@ Das "Custom Properties" Plugin unterstützt auch, dass mehrere Werte für eine E Hierbei werden die Werte mittels Tab bzw. einem `\t` separiert. Die Werte einer Eigenschaft werden in der jeweiligen Umgebungsvariable ebenfalls per Tab separiert gesetzt. +#### Pull-Request-Labels + +Bei Pull-Request-Builds werden Labels des SCM-Manager Pull Requests als Umgebungsvariablen gesetzt. +`SCMM_PR_LABELS` enthält alle Labels als JSON-Array und erhält damit die exakten Werte und die Reihenfolge aus dem SCM-Manager. +Für einfachen Zugriff werden zusätzlich `SCMM_PR_LABEL_COUNT` und durchnummerierte Variablen `SCMM_PR_LABEL_1`, `SCMM_PR_LABEL_2` usw. gesetzt. + +Beispiel: + +```groovy +pipeline { + agent any + + stages { + stage('Labels') { + steps { + script { + def labels = new groovy.json.JsonSlurperClassic().parseText(env.SCMM_PR_LABELS ?: '[]') + if (labels.contains('backend')) { + echo 'Backend-Label ist gesetzt' + } + } + } + } + } +} +``` + ### Organization Folders – Namespaces Sollen für alle Repositorys eines **kompletten Namespaces** im SCM-Manager Jobs erzeugt werden, kann ein **Organization Folder**-Job mit einem SCM-Manager-Namespace als Quelle genutzt werden. Dieser prüft alle Repositorys in einem gegebenen Namespace und erzeugt entsprechende Multibranch-Pipelines, wenn im Wurzelverzeichnis des Repositorys eine `Jenkinsfile` gefunden wurde. diff --git a/src/main/java/com/cloudogu/scmmanager/scm/api/PullRequest.java b/src/main/java/com/cloudogu/scmmanager/scm/api/PullRequest.java index 62c2dc6..5c0e41f 100644 --- a/src/main/java/com/cloudogu/scmmanager/scm/api/PullRequest.java +++ b/src/main/java/com/cloudogu/scmmanager/scm/api/PullRequest.java @@ -1,6 +1,8 @@ package com.cloudogu.scmmanager.scm.api; import de.otto.edison.hal.HalRepresentation; +import java.util.Collections; +import java.util.List; import java.util.Objects; public class PullRequest extends HalRepresentation implements ScmManagerObservable { @@ -11,6 +13,8 @@ public class PullRequest extends HalRepresentation implements ScmManagerObservab private String target; + private List labels = Collections.emptyList(); + private CloneInformation cloneInformation; private Branch sourceBranch; @@ -53,6 +57,13 @@ public String getTarget() { return target; } + public List getLabels() { + if (labels == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(labels); + } + @Override public ScmManagerPullRequestHead head() { if (head == null) { @@ -60,7 +71,8 @@ public ScmManagerPullRequestHead head() { cloneInformation, id, new ScmManagerHead(cloneInformation, target), - new ScmManagerHead(cloneInformation, source)); + new ScmManagerHead(cloneInformation, source), + getLabels()); } return head; } @@ -79,6 +91,7 @@ public boolean equals(Object o) { return Objects.equals(id, that.id) && Objects.equals(source, that.source) && Objects.equals(target, that.target) + && Objects.equals(getLabels(), that.getLabels()) && Objects.equals(cloneInformation, that.cloneInformation) && Objects.equals(sourceBranch, that.sourceBranch) && Objects.equals(targetBranch, that.targetBranch) @@ -87,6 +100,7 @@ public boolean equals(Object o) { @Override public int hashCode() { - return Objects.hash(super.hashCode(), id, source, target, cloneInformation, sourceBranch, targetBranch, head); + return Objects.hash( + super.hashCode(), id, source, target, getLabels(), cloneInformation, sourceBranch, targetBranch, head); } } diff --git a/src/main/java/com/cloudogu/scmmanager/scm/api/ScmManagerPullRequestHead.java b/src/main/java/com/cloudogu/scmmanager/scm/api/ScmManagerPullRequestHead.java index 7616b4e..b288873 100644 --- a/src/main/java/com/cloudogu/scmmanager/scm/api/ScmManagerPullRequestHead.java +++ b/src/main/java/com/cloudogu/scmmanager/scm/api/ScmManagerPullRequestHead.java @@ -1,6 +1,9 @@ package com.cloudogu.scmmanager.scm.api; import edu.umd.cs.findbugs.annotations.NonNull; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import jenkins.scm.api.SCMHeadOrigin; import jenkins.scm.api.mixin.ChangeRequestCheckoutStrategy; import jenkins.scm.api.mixin.ChangeRequestSCMHead2; @@ -14,17 +17,28 @@ public class ScmManagerPullRequestHead extends ScmManagerHead implements ChangeR private final ScmManagerHead target; private final ScmManagerHead source; + private final List labels; public ScmManagerPullRequestHead( @NonNull CloneInformation cloneInformation, @NonNull String id, @NonNull ScmManagerHead target, ScmManagerHead source) { + this(cloneInformation, id, target, source, Collections.emptyList()); + } + + public ScmManagerPullRequestHead( + @NonNull CloneInformation cloneInformation, + @NonNull String id, + @NonNull ScmManagerHead target, + ScmManagerHead source, + List labels) { // ?? why PullRequest/... super(cloneInformation, "PR-" + id); this.id = id; this.target = target; this.source = source; + this.labels = labels == null ? Collections.emptyList() : new ArrayList<>(labels); } @NonNull @@ -43,6 +57,13 @@ public ScmManagerHead getSource() { return source; } + public List getLabels() { + if (labels == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(labels); + } + @NonNull @Override public ChangeRequestCheckoutStrategy getCheckoutStrategy() { diff --git a/src/main/java/com/cloudogu/scmmanager/scm/env/PullRequestLabelsEnvContributor.java b/src/main/java/com/cloudogu/scmmanager/scm/env/PullRequestLabelsEnvContributor.java new file mode 100644 index 0000000..68a8fa3 --- /dev/null +++ b/src/main/java/com/cloudogu/scmmanager/scm/env/PullRequestLabelsEnvContributor.java @@ -0,0 +1,113 @@ +package com.cloudogu.scmmanager.scm.env; + +import com.cloudogu.scmmanager.scm.ScmManagerApiData; +import com.cloudogu.scmmanager.scm.api.PullRequest; +import com.cloudogu.scmmanager.scm.api.Repository; +import com.cloudogu.scmmanager.scm.api.ScmManagerApi; +import com.cloudogu.scmmanager.scm.api.ScmManagerApiFactory; +import com.cloudogu.scmmanager.scm.api.ScmManagerPullRequestHead; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import hudson.EnvVars; +import hudson.Extension; +import hudson.model.EnvironmentContributor; +import hudson.model.ItemGroup; +import hudson.model.Job; +import hudson.model.Run; +import hudson.model.TaskListener; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import javax.annotation.Nonnull; +import jenkins.branch.Branch; +import jenkins.scm.api.SCMHead; +import lombok.extern.slf4j.Slf4j; +import org.jenkinsci.plugins.workflow.multibranch.BranchJobProperty; + +@Extension(optional = true) +@Slf4j +public class PullRequestLabelsEnvContributor extends EnvironmentContributor { + + private static final String ENV_LABELS = "SCMM_PR_LABELS"; + private static final String ENV_LABEL_COUNT = "SCMM_PR_LABEL_COUNT"; + private static final String ENV_LABEL_PREFIX = "SCMM_PR_LABEL_"; + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + private final ScmManagerApiFactory apiFactory; + + public PullRequestLabelsEnvContributor() { + this(new ScmManagerApiFactory()); + } + + PullRequestLabelsEnvContributor(ScmManagerApiFactory apiFactory) { + this.apiFactory = apiFactory; + } + + @Override + public void buildEnvironmentFor(@Nonnull Run run, @Nonnull EnvVars envs, @Nonnull TaskListener listener) { + ScmManagerPullRequestHead pullRequestHead = getPullRequestHead(run); + if (pullRequestHead == null) { + return; + } + + List labels = fetchLabels(run, pullRequestHead); + envs.put(ENV_LABELS, toJson(labels)); + envs.put(ENV_LABEL_COUNT, String.valueOf(labels.size())); + + for (int i = 0; i < labels.size(); i++) { + envs.put(ENV_LABEL_PREFIX + (i + 1), labels.get(i)); + } + } + + private ScmManagerPullRequestHead getPullRequestHead(Run run) { + Job parent = run.getParent(); + BranchJobProperty branchJobProperty = parent.getProperty(BranchJobProperty.class); + if (branchJobProperty == null) { + return null; + } + + Branch branch = branchJobProperty.getBranch(); + SCMHead head = branch.getHead(); + if (head instanceof ScmManagerPullRequestHead) { + return (ScmManagerPullRequestHead) head; + } + + return null; + } + + private List fetchLabels(Run run, ScmManagerPullRequestHead pullRequestHead) { + Job job = run.getParent(); + ScmManagerApiData apiData = job.getAction(ScmManagerApiData.class); + if (apiData == null) { + return pullRequestHead.getLabels(); + } + + try { + ScmManagerApi client = apiFactory.create( + (ItemGroup) job.getParent(), apiData.getServerUrl(), apiData.getCredentialsId()); + Repository repository = client.getRepository(apiData.getNamespace(), apiData.getName()) + .get(); + CompletableFuture pullRequest = client.getPullRequest(repository, pullRequestHead.getId()); + if (pullRequest == null) { + return pullRequestHead.getLabels(); + } + return pullRequest.get().getLabels(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.error("could not fetch labels for pull request {}. Error: {}", pullRequestHead.getId(), e.getMessage()); + } catch (ExecutionException e) { + log.error("could not fetch labels for pull request {}. Error: {}", pullRequestHead.getId(), e.getMessage()); + } + + return pullRequestHead.getLabels(); + } + + private String toJson(List labels) { + try { + return OBJECT_MAPPER.writeValueAsString(labels); + } catch (JsonProcessingException e) { + log.warn("could not serialize SCM-Manager pull request labels", e); + return "[]"; + } + } +} diff --git a/src/test/java/com/cloudogu/scmmanager/scm/DescriptorEndpointSecurityTest.java b/src/test/java/com/cloudogu/scmmanager/scm/DescriptorEndpointSecurityTest.java new file mode 100644 index 0000000..7c746c3 --- /dev/null +++ b/src/test/java/com/cloudogu/scmmanager/scm/DescriptorEndpointSecurityTest.java @@ -0,0 +1,88 @@ +package com.cloudogu.scmmanager.scm; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.net.URL; +import java.util.List; +import jenkins.model.Jenkins; +import okhttp3.mockwebserver.MockWebServer; +import org.htmlunit.HttpMethod; +import org.htmlunit.Page; +import org.htmlunit.WebRequest; +import org.htmlunit.util.NameValuePair; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.jvnet.hudson.test.JenkinsRule; +import org.jvnet.hudson.test.MockAuthorizationStrategy; +import org.jvnet.hudson.test.junit.jupiter.WithJenkins; + +@WithJenkins +class DescriptorEndpointSecurityTest { + + private static final String LOW_PRIVILEGE_USER = "lowpriv"; + private static final String NAVIGATOR_DESCRIPTOR = + "descriptorByName/com.cloudogu.scmmanager.scm.ScmManagerNavigator"; + private static final String SOURCE_DESCRIPTOR = "descriptorByName/com.cloudogu.scmmanager.scm.ScmManagerSource"; + + private JenkinsRule j; + + @BeforeEach + void beforeEach(JenkinsRule rule) { + j = rule; + j.jenkins.setSecurityRealm(j.createDummySecurityRealm()); + j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy() + .grant(Jenkins.READ) + .everywhere() + .to(LOW_PRIVILEGE_USER) + .grant(Jenkins.ADMINISTER) + .everywhere() + .to("admin")); + } + + @Test + void shouldRequirePostForServerUrlChecks() throws Exception { + assertGetIsRejected(NAVIGATOR_DESCRIPTOR + "/checkServerUrl?value=http://example.com"); + assertGetIsRejected(SOURCE_DESCRIPTOR + "/checkServerUrl?value=http://example.com"); + } + + @Test + void shouldSkipLowPrivilegeCredentialChecksBeforeConnectingToServerUrl() throws Exception { + try (MockWebServer server = new MockWebServer()) { + server.start(); + String serverUrl = server.url("/").toString(); + + assertPostIsOk( + NAVIGATOR_DESCRIPTOR + "/checkCredentialsId", + List.of(new NameValuePair("serverUrl", serverUrl), new NameValuePair("value", "scm-creds"))); + assertPostIsOk( + SOURCE_DESCRIPTOR + "/checkCredentialsId", + List.of(new NameValuePair("serverUrl", serverUrl), new NameValuePair("value", "scm-creds"))); + + assertThat(server.getRequestCount()).isZero(); + } + } + + private void assertGetIsRejected(String path) throws Exception { + JenkinsRule.WebClient webClient = lowPrivilegeWebClient(); + + Page page = webClient.goTo(path, "text/html"); + + assertThat(page.getWebResponse().getStatusCode()).isEqualTo(405); + } + + private void assertPostIsOk(String path, List parameters) throws Exception { + JenkinsRule.WebClient webClient = lowPrivilegeWebClient(); + WebRequest request = new WebRequest(new URL(j.getURL(), path), HttpMethod.POST); + request.setRequestParameters(parameters); + + Page page = webClient.getPage(webClient.addCrumb(request)); + + assertThat(page.getWebResponse().getStatusCode()).isEqualTo(200); + } + + private JenkinsRule.WebClient lowPrivilegeWebClient() { + return j.createWebClient() + .withBasicCredentials(LOW_PRIVILEGE_USER, LOW_PRIVILEGE_USER) + .withThrowExceptionOnFailingStatusCode(false); + } +} diff --git a/src/test/java/com/cloudogu/scmmanager/scm/api/ScmManagerApiTest.java b/src/test/java/com/cloudogu/scmmanager/scm/api/ScmManagerApiTest.java index 64c00d6..384917b 100644 --- a/src/test/java/com/cloudogu/scmmanager/scm/api/ScmManagerApiTest.java +++ b/src/test/java/com/cloudogu/scmmanager/scm/api/ScmManagerApiTest.java @@ -5,6 +5,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; import jenkins.scm.api.SCMFile; import org.junit.jupiter.api.Test; @@ -207,6 +208,8 @@ void shouldLoadPullRequests() throws Exception { assertThat(pullRequest.getId()).isEqualTo("1"); assertThat(pullRequest.getSource()).isEqualTo("develop"); assertThat(pullRequest.getTarget()).isEqualTo("master"); + assertThat(pullRequest.getLabels()).containsExactly("backend", "needs-review"); + assertThat(pullRequest.head().getLabels()).containsExactly("backend", "needs-review"); } @Test @@ -225,6 +228,16 @@ void shouldLoadSinglePullRequest() throws Exception { assertThat(pullRequest.getId()).isEqualTo("1"); assertThat(pullRequest.getSource()).isEqualTo("develop"); assertThat(pullRequest.getTarget()).isEqualTo("master"); + assertThat(pullRequest.getLabels()).containsExactly("backend", "needs-review"); + assertThat(pullRequest.head().getLabels()).containsExactly("backend", "needs-review"); + } + + @Test + void shouldUseEmptyLabelListWhenLabelsAreMissing() throws Exception { + PullRequest pullRequest = new ObjectMapper() + .readValue("{\"id\":\"1\",\"source\":\"develop\",\"target\":\"master\"}", PullRequest.class); + + assertThat(pullRequest.getLabels()).isEmpty(); } @Test diff --git a/src/test/java/com/cloudogu/scmmanager/scm/env/PullRequestLabelsEnvContributorTest.java b/src/test/java/com/cloudogu/scmmanager/scm/env/PullRequestLabelsEnvContributorTest.java new file mode 100644 index 0000000..1fde3f2 --- /dev/null +++ b/src/test/java/com/cloudogu/scmmanager/scm/env/PullRequestLabelsEnvContributorTest.java @@ -0,0 +1,165 @@ +package com.cloudogu.scmmanager.scm.env; + +import static java.util.concurrent.CompletableFuture.completedFuture; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import com.cloudogu.scmmanager.scm.ScmManagerApiData; +import com.cloudogu.scmmanager.scm.api.CloneInformation; +import com.cloudogu.scmmanager.scm.api.PullRequest; +import com.cloudogu.scmmanager.scm.api.Repository; +import com.cloudogu.scmmanager.scm.api.ScmManagerApi; +import com.cloudogu.scmmanager.scm.api.ScmManagerApiFactory; +import com.cloudogu.scmmanager.scm.api.ScmManagerHead; +import com.cloudogu.scmmanager.scm.api.ScmManagerPullRequestHead; +import hudson.EnvVars; +import hudson.model.TaskListener; +import hudson.scm.SCM; +import java.util.List; +import jenkins.branch.Branch; +import jenkins.model.Jenkins; +import jenkins.scm.api.SCMHead; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.jenkinsci.plugins.workflow.job.WorkflowRun; +import org.jenkinsci.plugins.workflow.multibranch.BranchJobProperty; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class PullRequestLabelsEnvContributorTest { + + private static final String SERVER_URL = "http://localhost:8080"; + private static final String CREDENTIALS_ID = "CREDS_ID"; + private static final String NAMESPACE = "NAMESPACE"; + private static final String NAME = "NAME"; + private static final CloneInformation CLONE_INFORMATION = new CloneInformation("git", "http://example.com/repo"); + + @Mock + private ScmManagerApiFactory apiFactory; + + @Mock + private ScmManagerApi api; + + @Mock + private Jenkins owner; + + @Mock + private WorkflowJob job; + + @Mock + private WorkflowRun run; + + @Mock + private EnvVars envVars; + + @Mock + private TaskListener listener; + + @Mock + private BranchJobProperty branchJobProperty; + + private PullRequestLabelsEnvContributor envContributor; + + @BeforeEach + void beforeEach() { + lenient().when(job.getParent()).thenReturn(owner); + lenient().when(run.getParent()).thenReturn(job); + envContributor = new PullRequestLabelsEnvContributor(apiFactory); + } + + @Test + void shouldInjectCurrentPullRequestLabelsFromApi() { + useBranch(pullRequestHead(List.of("old-label"))); + Repository repository = setupApiCall(pullRequestWithLabels("backend", "needs review", "comma,label")); + + envContributor.buildEnvironmentFor(run, envVars, listener); + + verify(api).getPullRequest(repository, "42"); + verify(envVars).put("SCMM_PR_LABELS", "[\"backend\",\"needs review\",\"comma,label\"]"); + verify(envVars).put("SCMM_PR_LABEL_COUNT", "3"); + verify(envVars).put("SCMM_PR_LABEL_1", "backend"); + verify(envVars).put("SCMM_PR_LABEL_2", "needs review"); + verify(envVars).put("SCMM_PR_LABEL_3", "comma,label"); + verifyNoMoreInteractions(envVars); + } + + @Test + void shouldFallBackToPullRequestHeadLabelsWhenApiDataIsMissing() { + useBranch(pullRequestHead(List.of("backend", "needs review", "comma,label"))); + + envContributor.buildEnvironmentFor(run, envVars, listener); + + verifyNoInteractions(apiFactory); + verify(envVars).put("SCMM_PR_LABELS", "[\"backend\",\"needs review\",\"comma,label\"]"); + verify(envVars).put("SCMM_PR_LABEL_COUNT", "3"); + verify(envVars).put("SCMM_PR_LABEL_1", "backend"); + verify(envVars).put("SCMM_PR_LABEL_2", "needs review"); + verify(envVars).put("SCMM_PR_LABEL_3", "comma,label"); + verifyNoMoreInteractions(envVars); + } + + @Test + void shouldInjectEmptyLabelListForPullRequestWithoutLabels() { + useBranch(pullRequestHead(List.of())); + + envContributor.buildEnvironmentFor(run, envVars, listener); + + verifyNoInteractions(apiFactory); + verify(envVars).put("SCMM_PR_LABELS", "[]"); + verify(envVars).put("SCMM_PR_LABEL_COUNT", "0"); + verifyNoMoreInteractions(envVars); + } + + @Test + void shouldNotInjectLabelsForNonPullRequestBuild() { + useBranch(new ScmManagerHead(CLONE_INFORMATION, "main")); + + envContributor.buildEnvironmentFor(run, envVars, listener); + + verifyNoInteractions(envVars); + } + + @Test + void shouldNotInjectLabelsWithoutBranchJobProperty() { + envContributor.buildEnvironmentFor(run, envVars, listener); + + verifyNoInteractions(envVars); + } + + private Repository setupApiCall(PullRequest pullRequest) { + Repository repository = new Repository(NAMESPACE, NAME, "git"); + when(job.getAction(ScmManagerApiData.class)) + .thenReturn(new ScmManagerApiData(SERVER_URL, CREDENTIALS_ID, NAMESPACE, NAME)); + when(apiFactory.create(owner, SERVER_URL, CREDENTIALS_ID)).thenReturn(api); + when(api.getRepository(NAMESPACE, NAME)).thenReturn(completedFuture(repository)); + when(api.getPullRequest(repository, "42")).thenReturn(completedFuture(pullRequest)); + return repository; + } + + private ScmManagerPullRequestHead pullRequestHead(List labels) { + return new ScmManagerPullRequestHead( + CLONE_INFORMATION, + "42", + new ScmManagerHead(CLONE_INFORMATION, "main"), + new ScmManagerHead(CLONE_INFORMATION, "feature"), + labels); + } + + private PullRequest pullRequestWithLabels(String... labels) { + PullRequest pullRequest = mock(PullRequest.class); + when(pullRequest.getLabels()).thenReturn(List.of(labels)); + return pullRequest; + } + + private void useBranch(SCMHead head) { + when(job.getProperty(BranchJobProperty.class)).thenReturn(branchJobProperty); + when(branchJobProperty.getBranch()).thenReturn(new Branch("source-id", head, mock(SCM.class), List.of())); + } +} diff --git a/src/test/resources/mappings/pullRequest.json b/src/test/resources/mappings/pullRequest.json index 3acb19c..dbf807c 100644 --- a/src/test/resources/mappings/pullRequest.json +++ b/src/test/resources/mappings/pullRequest.json @@ -10,7 +10,7 @@ }, "response": { "status": 200, - "body": "{\"id\":\"1\",\"author\":{\"id\":\"sdorra\",\"displayName\":\"Sebastian Sdorra\",\"mail\":\"sebastian.sdorra@cloudogu.com\"},\"source\":\"develop\",\"target\":\"master\",\"title\":\"develop => master\",\"description\":null,\"creationDate\":\"2020-06-24T05:24:24.340Z\",\"lastModified\":null,\"status\":\"OPEN\",\"reviewer\":[],\"tasks\":{\"todo\":0,\"done\":0},\"sourceRevision\":null,\"targetRevision\":null,\"markedAsReviewed\":[],\"emergencyMerged\":false,\"ignoredMergeObstacles\":null,\"_links\":{\"self\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1\"},\"comments\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/comments/\"},\"events\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/events\"},\"approve\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/approve\"},\"subscription\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/subscription\"},\"update\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1\"},\"reject\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/reject\"},\"mergeCheck\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/merge-check\"},\"mergeConflicts\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/conflicts\"},\"defaultCommitMessage\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/commit-message\"},\"merge\":[{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=MERGE_COMMIT\",\"name\":\"MERGE_COMMIT\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=FAST_FORWARD_IF_POSSIBLE\",\"name\":\"FAST_FORWARD_IF_POSSIBLE\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=SQUASH\",\"name\":\"SQUASH\"}],\"emergencyMerge\":[{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=MERGE_COMMIT\",\"name\":\"MERGE_COMMIT\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=FAST_FORWARD_IF_POSSIBLE\",\"name\":\"FAST_FORWARD_IF_POSSIBLE\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=SQUASH\",\"name\":\"SQUASH\"}],\"workflowResult\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/workflow/\"},\"reviewMark\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/review-mark/{path}\",\"templated\":true},\"sourceBranch\":{\"href\":\"/scm/api/v2/repositories/jenkins-plugin/hello-shell/branches/develop\"},\"targetBranch\":{\"href\":\"/scm/api/v2/repositories/jenkins-plugin/hello-shell/branches/master\"},\"ciStatus\":{\"href\":\"/scm/api/v2/ci/jenkins-plugin/hello-shell/pullrequest/1\"},\"teamscaleFindings\":{\"href\":\"/scm/api/v2/teamscale/pull-request/jenkins-plugin/hello-shell/1/findings\"}}}", + "body": "{\"id\":\"1\",\"author\":{\"id\":\"sdorra\",\"displayName\":\"Sebastian Sdorra\",\"mail\":\"sebastian.sdorra@cloudogu.com\"},\"source\":\"develop\",\"target\":\"master\",\"title\":\"develop => master\",\"description\":null,\"creationDate\":\"2020-06-24T05:24:24.340Z\",\"lastModified\":null,\"status\":\"OPEN\",\"reviewer\":[],\"tasks\":{\"todo\":0,\"done\":0},\"sourceRevision\":null,\"targetRevision\":null,\"markedAsReviewed\":[],\"emergencyMerged\":false,\"ignoredMergeObstacles\":null,\"_links\":{\"self\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1\"},\"comments\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/comments/\"},\"events\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/events\"},\"approve\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/approve\"},\"subscription\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/subscription\"},\"update\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1\"},\"reject\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/reject\"},\"mergeCheck\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/merge-check\"},\"mergeConflicts\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/conflicts\"},\"defaultCommitMessage\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/commit-message\"},\"merge\":[{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=MERGE_COMMIT\",\"name\":\"MERGE_COMMIT\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=FAST_FORWARD_IF_POSSIBLE\",\"name\":\"FAST_FORWARD_IF_POSSIBLE\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=SQUASH\",\"name\":\"SQUASH\"}],\"emergencyMerge\":[{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=MERGE_COMMIT\",\"name\":\"MERGE_COMMIT\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=FAST_FORWARD_IF_POSSIBLE\",\"name\":\"FAST_FORWARD_IF_POSSIBLE\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=SQUASH\",\"name\":\"SQUASH\"}],\"workflowResult\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/workflow/\"},\"reviewMark\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/review-mark/{path}\",\"templated\":true},\"sourceBranch\":{\"href\":\"/scm/api/v2/repositories/jenkins-plugin/hello-shell/branches/develop\"},\"targetBranch\":{\"href\":\"/scm/api/v2/repositories/jenkins-plugin/hello-shell/branches/master\"},\"ciStatus\":{\"href\":\"/scm/api/v2/ci/jenkins-plugin/hello-shell/pullrequest/1\"},\"teamscaleFindings\":{\"href\":\"/scm/api/v2/teamscale/pull-request/jenkins-plugin/hello-shell/1/findings\"}},\"labels\":[\"backend\",\"needs-review\"]}", "headers": { "Content-Type": "application/vnd.scmm-pullRequest+json;v=2" } diff --git a/src/test/resources/mappings/pullRequests.json b/src/test/resources/mappings/pullRequests.json index 3c5971a..6ce8857 100644 --- a/src/test/resources/mappings/pullRequests.json +++ b/src/test/resources/mappings/pullRequests.json @@ -10,7 +10,7 @@ }, "response": { "status": 200, - "body": "{\"_links\":{\"self\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell\"},\"create\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell\"}},\"_embedded\":{\"pullRequests\":[{\"id\":\"1\",\"author\":{\"id\":\"sdorra\",\"displayName\":\"Sebastian Sdorra\",\"mail\":\"sebastian.sdorra@cloudogu.com\"},\"source\":\"develop\",\"target\":\"master\",\"title\":\"develop => master\",\"description\":null,\"creationDate\":\"2020-06-24T05:24:24.340Z\",\"lastModified\":null,\"status\":\"OPEN\",\"reviewer\":[],\"tasks\":{\"todo\":0,\"done\":0},\"sourceRevision\":null,\"targetRevision\":null,\"markedAsReviewed\":[],\"emergencyMerged\":false,\"ignoredMergeObstacles\":null,\"_links\":{\"self\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1\"},\"comments\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/comments/\"},\"events\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/events\"},\"approve\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/approve\"},\"subscription\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/subscription\"},\"update\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1\"},\"reject\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/reject\"},\"mergeCheck\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/merge-check\"},\"mergeConflicts\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/conflicts\"},\"defaultCommitMessage\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/commit-message\"},\"merge\":[{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=MERGE_COMMIT\",\"name\":\"MERGE_COMMIT\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=FAST_FORWARD_IF_POSSIBLE\",\"name\":\"FAST_FORWARD_IF_POSSIBLE\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=SQUASH\",\"name\":\"SQUASH\"}],\"emergencyMerge\":[{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=MERGE_COMMIT\",\"name\":\"MERGE_COMMIT\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=FAST_FORWARD_IF_POSSIBLE\",\"name\":\"FAST_FORWARD_IF_POSSIBLE\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=SQUASH\",\"name\":\"SQUASH\"}],\"workflowResult\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/workflow/\"},\"reviewMark\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/review-mark/{path}\",\"templated\":true},\"sourceBranch\":{\"href\":\"/scm/api/v2/repositories/jenkins-plugin/hello-shell/branches/develop\"},\"targetBranch\":{\"href\":\"/scm/api/v2/repositories/jenkins-plugin/hello-shell/branches/master\"},\"ciStatus\":{\"href\":\"/scm/api/v2/ci/jenkins-plugin/hello-shell/pullrequest/1\"},\"teamscaleFindings\":{\"href\":\"/scm/api/v2/teamscale/pull-request/jenkins-plugin/hello-shell/1/findings\"}}}]}}", + "body": "{\"_links\":{\"self\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell\"},\"create\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell\"}},\"_embedded\":{\"pullRequests\":[{\"id\":\"1\",\"author\":{\"id\":\"sdorra\",\"displayName\":\"Sebastian Sdorra\",\"mail\":\"sebastian.sdorra@cloudogu.com\"},\"source\":\"develop\",\"target\":\"master\",\"title\":\"develop => master\",\"description\":null,\"creationDate\":\"2020-06-24T05:24:24.340Z\",\"lastModified\":null,\"status\":\"OPEN\",\"reviewer\":[],\"tasks\":{\"todo\":0,\"done\":0},\"sourceRevision\":null,\"targetRevision\":null,\"markedAsReviewed\":[],\"emergencyMerged\":false,\"ignoredMergeObstacles\":null,\"_links\":{\"self\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1\"},\"comments\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/comments/\"},\"events\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/events\"},\"approve\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/approve\"},\"subscription\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/subscription\"},\"update\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1\"},\"reject\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/reject\"},\"mergeCheck\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/merge-check\"},\"mergeConflicts\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/conflicts\"},\"defaultCommitMessage\":{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/commit-message\"},\"merge\":[{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=MERGE_COMMIT\",\"name\":\"MERGE_COMMIT\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=FAST_FORWARD_IF_POSSIBLE\",\"name\":\"FAST_FORWARD_IF_POSSIBLE\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1?strategy=SQUASH\",\"name\":\"SQUASH\"}],\"emergencyMerge\":[{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=MERGE_COMMIT\",\"name\":\"MERGE_COMMIT\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=FAST_FORWARD_IF_POSSIBLE\",\"name\":\"FAST_FORWARD_IF_POSSIBLE\"},{\"href\":\"/scm/api/v2/merge/jenkins-plugin/hello-shell/1/emergency?strategy=SQUASH\",\"name\":\"SQUASH\"}],\"workflowResult\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/workflow/\"},\"reviewMark\":{\"href\":\"/scm/api/v2/pull-requests/jenkins-plugin/hello-shell/1/review-mark/{path}\",\"templated\":true},\"sourceBranch\":{\"href\":\"/scm/api/v2/repositories/jenkins-plugin/hello-shell/branches/develop\"},\"targetBranch\":{\"href\":\"/scm/api/v2/repositories/jenkins-plugin/hello-shell/branches/master\"},\"ciStatus\":{\"href\":\"/scm/api/v2/ci/jenkins-plugin/hello-shell/pullrequest/1\"},\"teamscaleFindings\":{\"href\":\"/scm/api/v2/teamscale/pull-request/jenkins-plugin/hello-shell/1/findings\"}},\"labels\":[\"backend\",\"needs-review\"]}]}}", "headers": { "Content-Type": "application/vnd.scmm-pullRequest+json;v=2" }