4545import java .util .List ;
4646import java .util .Set ;
4747import java .util .TreeSet ;
48+ import java .util .logging .Logger ;
49+ import org .apache .commons .collections .CollectionUtils ;
4850import org .jenkinsci .plugins .plaincredentials .StringCredentials ;
4951
5052/**
5153 * Contains the webhook configuration
5254 */
5355public class WebhookConfiguration {
56+ private static final Logger logger = Logger .getLogger (WebhookConfiguration .class .getName ());
5457
5558 /**
5659 * The list of events available in Bitbucket Cloud.
@@ -81,6 +84,20 @@ public class WebhookConfiguration {
8184 HookEventType .SERVER_PULL_REQUEST_FROM_REF_UPDATED .getKey ()
8285 ));
8386
87+ // See https://help.moveworkforward.com/BPW/how-to-manage-configurations-using-post-webhooks-f#HowtomanageconfigurationsusingPostWebhooksforBitbucketAPIs?-Possibleeventtypes
88+ private static final List <String > PLUGIN_SERVER_EVENTS = Collections .unmodifiableList (Arrays .asList (
89+ "ABSTRACT_REPOSITORY_REFS_CHANGED" , // push event
90+ "BRANCH_CREATED" ,
91+ "BRANCH_DELETED" ,
92+ "PULL_REQUEST_DECLINED" ,
93+ "PULL_REQUEST_DELETED" ,
94+ "PULL_REQUEST_MERGED" ,
95+ "PULL_REQUEST_OPENED" ,
96+ "PULL_REQUEST_REOPENED" ,
97+ "PULL_REQUEST_UPDATED" ,
98+ "REPOSITORY_MIRROR_SYNCHRONIZED" , // not supported by the hookprocessor
99+ "TAG_CREATED" ));
100+
84101 /**
85102 * The list of events available in Bitbucket Server v6.5+.
86103 */
@@ -99,7 +116,7 @@ public class WebhookConfiguration {
99116 /**
100117 * The title of the webhook.
101118 */
102- private static final String description = "Jenkins hook" ;
119+ private static final String DESCRIPTION = "Jenkins hook" ;
103120
104121 /**
105122 * The comma separated list of committers to ignore.
@@ -121,43 +138,72 @@ public String getCommittersToIgnore() {
121138 boolean updateHook (BitbucketWebHook hook , BitbucketSCMSource owner ) {
122139 boolean updated = false ;
123140
141+ final String serverURL = owner .getServerUrl ();
142+ final String rootURL = BitbucketEndpointProvider .lookupEndpointJenkinsRootURL (serverURL );
124143 final String signatureSecret = getSecret (owner .getServerUrl ());
125144
126145 if (hook instanceof BitbucketCloudHook cloudHook ) {
127- if (!hook .getEvents ().containsAll (CLOUD_EVENTS )) {
128- Set <String > events = new TreeSet <>(hook .getEvents ());
129- events .addAll (CLOUD_EVENTS );
130- cloudHook .setEvents (new ArrayList <>(events ));
146+ String url = getCloudWebhookURL (serverURL , rootURL );
147+ if (!Objects .equal (hook .getUrl (), url )) {
148+ cloudHook .setUrl (url );
149+ updated = true ;
150+ }
151+
152+ List <String > events = hook .getEvents ();
153+ if (!events .containsAll (CLOUD_EVENTS )) {
154+ Set <String > newEvents = new TreeSet <>(events );
155+ newEvents .addAll (CLOUD_EVENTS );
156+ cloudHook .setEvents (new ArrayList <>(newEvents ));
157+ logger .info (() -> "Update cloud webhook because the following events was missing: " + CollectionUtils .subtract (CLOUD_EVENTS , events ));
131158 updated = true ;
132159 }
160+
133161 if (!Objects .equal (hook .getSecret (), signatureSecret )) {
134162 cloudHook .setSecret (signatureSecret );
135163 updated = true ;
136164 }
137- } else if (hook instanceof BitbucketPluginWebhook serverHook ) {
138- String hookCommittersToIgnore = Util .fixEmptyAndTrim (serverHook .getCommittersToIgnore ());
165+ } else if (hook instanceof BitbucketPluginWebhook pluginHook ) {
166+ String hookCommittersToIgnore = Util .fixEmptyAndTrim (pluginHook .getCommittersToIgnore ());
139167 String thisCommittersToIgnore = Util .fixEmptyAndTrim (committersToIgnore );
140168 if (!Objects .equal (thisCommittersToIgnore , hookCommittersToIgnore )) {
141- serverHook .setCommittersToIgnore (thisCommittersToIgnore );
169+ pluginHook .setCommittersToIgnore (thisCommittersToIgnore );
170+ updated = true ;
171+ }
172+
173+ String url = getServerWebhookURL (serverURL , rootURL );
174+ if (!url .equals (pluginHook .getUrl ())) {
175+ pluginHook .setUrl (url );
176+ updated = true ;
177+ }
178+
179+ if (!pluginHook .isActive ()) {
180+ pluginHook .setActive (true );
142181 updated = true ;
143182 }
144- } else if (hook instanceof BitbucketServerWebhook serverHook ) {
145- String serverURL = owner .getServerUrl ();
146- String url = getServerWebhookURL (serverURL , BitbucketEndpointProvider .lookupEndpointJenkinsRootURL (owner .getServerUrl ()));
147183
184+ List <String > supportedPluginEvents = getPluginServerEvents (serverURL );
185+ List <String > events = pluginHook .getEvents ();
186+ if (!events .containsAll (supportedPluginEvents )) {
187+ Set <String > newEvents = new TreeSet <>(events );
188+ newEvents .addAll (supportedPluginEvents );
189+ pluginHook .setEvents (new ArrayList <>(newEvents ));
190+ logger .info (() -> "Update plugin webhook because the following events was missing: " + CollectionUtils .subtract (supportedPluginEvents , events ));
191+ updated = true ;
192+ }
193+ } else if (hook instanceof BitbucketServerWebhook serverHook ) {
194+ String url = getServerWebhookURL (serverURL , rootURL );
148195 if (!url .equals (serverHook .getUrl ())) {
149196 serverHook .setUrl (url );
150197 updated = true ;
151198 }
152199
200+ List <String > supportedNativeEvents = getNativeServerEvents (serverURL );
153201 List <String > events = serverHook .getEvents ();
154- if (events == null ) {
155- serverHook .setEvents (getNativeServerEvents (serverURL ));
156- updated = true ;
157- } else if (!events .containsAll (getNativeServerEvents (serverURL ))) {
202+ if (!events .containsAll (supportedNativeEvents )) {
158203 Set <String > newEvents = new TreeSet <>(events );
159- newEvents .addAll (getNativeServerEvents ( serverURL ) );
204+ newEvents .addAll (supportedNativeEvents );
160205 serverHook .setEvents (new ArrayList <>(newEvents ));
206+ logger .info (() -> "Update native webhook because the following events was missing: " + CollectionUtils .subtract (supportedNativeEvents , events ));
161207 updated = true ;
162208 }
163209
@@ -170,28 +216,29 @@ boolean updateHook(BitbucketWebHook hook, BitbucketSCMSource owner) {
170216 return updated ;
171217 }
172218
219+ @ NonNull
173220 public BitbucketWebHook getHook (BitbucketSCMSource owner ) {
174- final String serverUrl = owner .getServerUrl ();
175- final String rootUrl = BitbucketEndpointProvider .lookupEndpointJenkinsRootURL (owner . getServerUrl () );
221+ final String serverURL = owner .getServerUrl ();
222+ final String rootURL = BitbucketEndpointProvider .lookupEndpointJenkinsRootURL (serverURL );
176223 final String signatureSecret = getSecret (owner .getServerUrl ());
177224
178- if (BitbucketApiUtils .isCloud (serverUrl )) {
225+ if (BitbucketApiUtils .isCloud (serverURL )) {
179226 BitbucketCloudHook hook = new BitbucketCloudHook ();
180227 hook .setEvents (CLOUD_EVENTS );
181228 hook .setActive (true );
182- hook .setDescription (description );
183- hook .setUrl (rootUrl + BitbucketSCMSourcePushHookReceiver . FULL_PATH );
229+ hook .setDescription (DESCRIPTION );
230+ hook .setUrl (getCloudWebhookURL ( serverURL , rootURL ) );
184231 hook .setSecret (signatureSecret );
185232 return hook ;
186233 }
187234
188- switch (BitbucketServerEndpoint .findWebhookImplementation (serverUrl )) {
235+ switch (BitbucketServerEndpoint .findWebhookImplementation (serverURL )) {
189236 case NATIVE : {
190237 BitbucketServerWebhook hook = new BitbucketServerWebhook ();
191238 hook .setActive (true );
192- hook .setDescription (description );
193- hook .setEvents (getNativeServerEvents (serverUrl ));
194- hook .setUrl (getServerWebhookURL (serverUrl , rootUrl ));
239+ hook .setDescription (DESCRIPTION );
240+ hook .setEvents (getNativeServerEvents (serverURL ));
241+ hook .setUrl (getServerWebhookURL (serverURL , rootURL ));
195242 hook .setSecret (signatureSecret );
196243 return hook ;
197244 }
@@ -200,8 +247,8 @@ public BitbucketWebHook getHook(BitbucketSCMSource owner) {
200247 default : {
201248 BitbucketPluginWebhook hook = new BitbucketPluginWebhook ();
202249 hook .setActive (true );
203- hook .setDescription (description );
204- hook .setUrl (getServerWebhookURL (serverUrl , rootUrl ));
250+ hook .setDescription (DESCRIPTION );
251+ hook .setUrl (getServerWebhookURL (serverURL , rootURL ));
205252 hook .setCommittersToIgnore (committersToIgnore );
206253 return hook ;
207254 }
@@ -224,9 +271,13 @@ private String getSecret(@NonNull String serverURL) {
224271 return null ;
225272 }
226273
227- private static List <String > getNativeServerEvents (String serverUrl ) {
274+ private static List <String > getPluginServerEvents (String serverURL ) {
275+ return PLUGIN_SERVER_EVENTS ;
276+ }
277+
278+ private static List <String > getNativeServerEvents (String serverURL ) {
228279 BitbucketServerEndpoint endpoint = BitbucketEndpointProvider
229- .lookupEndpoint (serverUrl , BitbucketServerEndpoint .class )
280+ .lookupEndpoint (serverURL , BitbucketServerEndpoint .class )
230281 .orElse (null );
231282 if (endpoint != null ) {
232283 switch (endpoint .getServerVersion ()) {
@@ -256,12 +307,19 @@ private static List<String> getNativeServerEvents(String serverUrl) {
256307 return NATIVE_SERVER_EVENTS_v7 ;
257308 }
258309
259- private static String getServerWebhookURL (String serverUrl , String rootUrl ) {
260- return UriTemplate .buildFromTemplate (rootUrl )
310+ private static String getCloudWebhookURL (String serverURL , String rootURL ) {
311+ return UriTemplate .buildFromTemplate (rootURL )
312+ .template (BitbucketSCMSourcePushHookReceiver .FULL_PATH )
313+ .build ()
314+ .expand ();
315+ }
316+
317+ private static String getServerWebhookURL (String serverURL , String rootURL ) {
318+ return UriTemplate .buildFromTemplate (rootURL )
261319 .template (BitbucketSCMSourcePushHookReceiver .FULL_PATH )
262320 .query ("server_url" )
263321 .build ()
264- .set ("server_url" , serverUrl )
322+ .set ("server_url" , serverURL )
265323 .expand ();
266324 }
267325}
0 commit comments