Skip to content

Commit

Permalink
Extract event anonymous inner classes from webhook processor.
Browse files Browse the repository at this point in the history
Move test to JUnit5 and add processor unit test
Add unit test for hook processor when process pull request. Consider to add other events for tag, new commit and so on
  • Loading branch information
nfalco79 committed Jan 12, 2025
1 parent e2da34a commit 1c99e54
Show file tree
Hide file tree
Showing 23 changed files with 1,732 additions and 745 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,8 @@ private void setPrimaryCloneLinks(List<BitbucketHref> links) {
links.forEach(link -> {
if (StringUtils.startsWithIgnoreCase(link.getName(), "http")) {
// Remove the username from URL because it will be set into the GIT_URL variable
// credentials used to clone or for push/pull could be different than this will cause a failure
// credentials used to git clone or push/pull operation could be different than this (for example SSH)
// and will run into a failure
// Restore the behaviour before mirror link feature.
link.setHref(URLUtils.removeAuthority(link.getHref()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,26 @@
import com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource;
import com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSourceContext;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketCloudEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.BitbucketApiUtils;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerRepository;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.scm.SCM;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Logger;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMHeadEvent;
import jenkins.scm.api.SCMHeadObserver;
import jenkins.scm.api.SCMNavigator;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMSource;

abstract class NativeServerHeadEvent<P> extends SCMHeadEvent<P> {
private static final Logger LOGGER = Logger.getLogger(NativeServerHeadEvent.class.getName());

abstract class AbstractNativeServerSCMHeadEvent<P> extends SCMHeadEvent<P> {
@NonNull
private final String serverUrl;
private final String serverURL;

NativeServerHeadEvent(String serverUrl, Type type, P payload, String origin) {
AbstractNativeServerSCMHeadEvent(String serverURL, Type type, P payload, String origin) {
super(type, payload, origin);
this.serverUrl = serverUrl;
this.serverURL = serverURL;
}

@NonNull
Expand All @@ -66,7 +63,7 @@ public boolean isMatch(@NonNull SCMNavigator navigator) {

final BitbucketSCMNavigator bbNav = (BitbucketSCMNavigator) navigator;

return isServerUrlMatch(bbNav.getServerUrl()) && bbNav.getRepoOwner().equalsIgnoreCase(getRepository().getOwnerName());
return isServerURLMatch(bbNav.getServerUrl()) && bbNav.getRepoOwner().equalsIgnoreCase(getRepository().getOwnerName());
}

@Override
Expand All @@ -87,12 +84,12 @@ public Map<SCMHead, SCMRevision> heads(@NonNull SCMSource source) {
@NonNull
protected abstract Map<SCMHead, SCMRevision> heads(@NonNull BitbucketSCMSource source);

protected boolean isServerUrlMatch(String serverUrl) {
if (serverUrl == null || BitbucketCloudEndpoint.SERVER_URL.equals(serverUrl)) {
protected boolean isServerURLMatch(String serverURL) {
if (serverURL == null || BitbucketApiUtils.isCloud(serverURL)) {
return false; // this is Bitbucket Cloud, which is not handled by this processor
}

return serverUrl.equals(this.serverUrl);
return serverURL.equals(this.serverURL);
}

protected boolean eventMatchesRepo(BitbucketSCMSource source) {
Expand All @@ -111,7 +108,7 @@ private BitbucketSCMSource getMatchingBitbucketSource(SCMSource source) {
}

final BitbucketSCMSource src = (BitbucketSCMSource) source;
if (!isServerUrlMatch(src.getServerUrl())) {
if (!isServerURLMatch(src.getServerUrl())) {

Check warning on line 111 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/AbstractNativeServerSCMHeadEvent.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 49-111 are not covered by tests
return null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* The MIT License
*
* Copyright (c) 2016, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.cloudbees.jenkins.plugins.bitbucket.hooks;

import com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMNavigator;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketHref;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
import com.cloudbees.jenkins.plugins.bitbucket.client.events.BitbucketCloudPullRequestEvent;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.BitbucketApiUtils;
import com.cloudbees.jenkins.plugins.bitbucket.server.events.BitbucketServerPullRequestEvent;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.scm.SCM;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import jenkins.scm.api.SCMHeadEvent;
import jenkins.scm.api.SCMNavigator;
import org.apache.commons.lang.StringUtils;

abstract class AbstractSCMHeadEvent<P> extends SCMHeadEvent<P> {

protected AbstractSCMHeadEvent(Type type, P payload, String origin) {
super(type, payload, origin);
}

@Override
public boolean isMatch(@NonNull SCMNavigator navigator) {
if (!(navigator instanceof BitbucketSCMNavigator)) {
return false;
}
BitbucketSCMNavigator bbNav = (BitbucketSCMNavigator) navigator;
if (!isProjectKeyMatch(bbNav.getProjectKey())) {
return false;
}

if (!isServerURLMatch(bbNav.getServerUrl())) {

Check warning on line 58 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/AbstractSCMHeadEvent.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 58 is only partially covered, one branch is missing
return false;

Check warning on line 59 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/AbstractSCMHeadEvent.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 59 is not covered by tests
}
return bbNav.getRepoOwner().equalsIgnoreCase(getRepository().getOwnerName());
}

protected abstract BitbucketRepository getRepository();

private boolean isProjectKeyMatch(String projectKey) {
if (StringUtils.isBlank(projectKey)) {
return true;
}
BitbucketRepository repository = getRepository();
if (repository.getProject() != null) {

Check warning on line 71 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/AbstractSCMHeadEvent.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 71 is only partially covered, one branch is missing
return projectKey.equals(repository.getProject().getKey());
}
return true;

Check warning on line 74 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/AbstractSCMHeadEvent.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 74 is not covered by tests
}

protected boolean isServerURLMatch(String serverURL) {
if (serverURL == null || BitbucketApiUtils.isCloud(serverURL)) {

Check warning on line 78 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/AbstractSCMHeadEvent.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 78 is only partially covered, 2 branches are missing
// this is a Bitbucket cloud navigator
if (getPayload() instanceof BitbucketServerPullRequestEvent) {

Check warning on line 80 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/AbstractSCMHeadEvent.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 80 is only partially covered, one branch is missing
return false;
}
} else {
// this is a Bitbucket server navigator
if (getPayload() instanceof BitbucketCloudPullRequestEvent) {
return false;
}
Map<String, List<BitbucketHref>> links = getRepository().getLinks();
if (links != null && links.containsKey("self")) {
boolean matches = false;
for (BitbucketHref link: links.get("self")) {
try {
URI navUri = new URI(serverURL);
URI evtUri = new URI(link.getHref());
if (navUri.getHost().equalsIgnoreCase(evtUri.getHost())) {
matches = true;
break;
}
} catch (URISyntaxException e) {
// ignore
}
}
return matches;

Check warning on line 103 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/AbstractSCMHeadEvent.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 81-103 are not covered by tests
}
}
return true;
}

@Override
public boolean isMatch(@NonNull SCM scm) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,20 @@ public HttpResponse doNotify(StaplerRequest2 req) throws IOException {
LOGGER.log(Level.FINE, "X-Bitbucket-Type header / server_url request parameter not found. Bitbucket Cloud webhook incoming.");
}

HookProcessor hookProcessor = getHookProcessor(type);
try {
type.getProcessor().process(type, body, instanceType, origin, serverUrl);
hookProcessor.process(type, body, instanceType, origin, serverUrl);
} catch (AbstractMethodError e) {
type.getProcessor().process(body, instanceType);
hookProcessor.process(body, instanceType);

Check notice

Code scanning / CodeQL

Deprecated method or constructor invocation Note

Invoking
HookProcessor.process
should be avoided because it has been deprecated.
}
return HttpResponses.ok();
}

/* For test purpose */
HookProcessor getHookProcessor(HookEventType type) {
return type.getProcessor();

Check warning on line 116 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/BitbucketSCMSourcePushHookReceiver.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 109-116 are not covered by tests
}

@Override
public String getIconFileName() {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public enum HookEventType {
/**
* Sent when hitting the {@literal "Test connection"} button in Bitbucket Server. Apparently undocumented.
*/
SERVER_PING("diagnostics:ping", PingHookProcessor.class);
SERVER_PING("diagnostics:ping", NativeServerPingHookProcessor.class);


private final String key;
Expand All @@ -149,8 +149,8 @@ public static HookEventType fromString(String key) {

public HookProcessor getProcessor() {
try {
return (HookProcessor) clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
return (HookProcessor) clazz.getDeclaredConstructor().newInstance();
} catch (ReflectiveOperationException e) {

Check warning on line 153 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/HookEventType.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 152-153 are not covered by tests
throw new AssertionError("Can not instantiate hook payload processor: " + e.getMessage());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,15 @@ public void process(HookEventType type, String payload, BitbucketType instanceTy
* @param repository the repository name as configured in the SCMSource
*/
protected void scmSourceReIndex(final String owner, final String repository) {
try (ACLContext context = ACL.as(ACL.SYSTEM)) {
try (ACLContext context = ACL.as2(ACL.SYSTEM2)) {
boolean reindexed = false;
for (SCMSourceOwner scmOwner : SCMSourceOwners.all()) {
List<SCMSource> sources = scmOwner.getSCMSources();
for (SCMSource source : sources) {
// Search for the correct SCM source
if (source instanceof BitbucketSCMSource && ((BitbucketSCMSource) source).getRepoOwner().equalsIgnoreCase(owner)
&& ((BitbucketSCMSource) source).getRepository().equals(repository)) {
if (source instanceof BitbucketSCMSource scmSource
&& scmSource.getRepoOwner().equalsIgnoreCase(owner)
&& scmSource.getRepository().equals(repository)) {

Check warning on line 101 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/HookProcessor.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 93-101 are not covered by tests
LOGGER.log(Level.INFO, "Multibranch project found, reindexing " + scmOwner.getName());
scmOwner.onSCMSourceUpdated(source);
reindexed = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,17 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket.hooks;

import hudson.RestrictedSince;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

public class PingHookProcessor extends HookProcessor {
@Restricted(NoExternalUse.class)
@RestrictedSince("933.3.0")
public class NativeServerPingHookProcessor extends HookProcessor {

private static final Logger LOGGER = Logger.getLogger(PingHookProcessor.class.getName());
private static final Logger LOGGER = Logger.getLogger(NativeServerPingHookProcessor.class.getName());

Check warning on line 36 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/NativeServerPingHookProcessor.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 34-36 are not covered by tests

@Override
public void process(HookEventType hookEvent, String payload, BitbucketType instanceType, String origin) {
Expand Down
Loading

0 comments on commit 1c99e54

Please sign in to comment.