diff --git a/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/BitbucketPropertyPathNotificationExtractor.java b/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/BitbucketPropertyPathNotificationExtractor.java index a21b6929..1da0077c 100644 --- a/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/BitbucketPropertyPathNotificationExtractor.java +++ b/spring-cloud-config-monitor/src/main/java/org/springframework/cloud/config/monitor/BitbucketPropertyPathNotificationExtractor.java @@ -27,6 +27,7 @@ import org.springframework.util.StringUtils; /** * @author Spencer Gibb * @author Dave Syer + * @author Greg Jacobs * */ @Order(Ordered.LOWEST_PRECEDENCE - 100) @@ -39,6 +40,7 @@ public class BitbucketPropertyPathNotificationExtractor if (("repo:push".equals(headers.getFirst("X-Event-Key")) || "pullrequest:fulfilled".equals(headers.getFirst("X-Event-Key"))) && StringUtils.hasText(headers.getFirst("X-Hook-UUID"))) { + // Bitbucket cloud Object push = request.get("push"); if (push instanceof Map && ((Map)push).get("changes") instanceof Collection) { // Bitbucket doesn't tell us the files that changed so this is a @@ -46,6 +48,16 @@ public class BitbucketPropertyPathNotificationExtractor return new PropertyPathNotification("application.yml"); } } + else if ( ("repo:refs_changed".equals(headers.getFirst("X-Event-Key")) || + "pr:merged".equals(headers.getFirst("X-Event-Key"))) && + StringUtils.hasText(headers.getFirst("X-Request-Id"))) { + //Bitbucket server + if (request.get("changes") instanceof Collection) { + // Bitbucket doesn't tell us the files that changed so this is a + // broadcast to all apps + return new PropertyPathNotification("application.yml"); + } + } return null; } diff --git a/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/BitbucketPropertyPathNotificationExtractorTests.java b/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/BitbucketPropertyPathNotificationExtractorTests.java index 3cc6c6be..41b27729 100644 --- a/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/BitbucketPropertyPathNotificationExtractorTests.java +++ b/spring-cloud-config-monitor/src/test/java/org/springframework/cloud/config/monitor/BitbucketPropertyPathNotificationExtractorTests.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertNull; import java.util.Map; import java.util.UUID; +import org.junit.Before; import org.junit.Test; import org.springframework.core.io.ClassPathResource; import org.springframework.http.HttpHeaders; @@ -33,13 +34,19 @@ import com.fasterxml.jackson.databind.ObjectMapper; /** * @author Spencer Gibb * @author Dave Syer + * @author Greg Jacobs * */ public class BitbucketPropertyPathNotificationExtractorTests { private BitbucketPropertyPathNotificationExtractor extractor = new BitbucketPropertyPathNotificationExtractor(); - private HttpHeaders headers = new HttpHeaders(); + private HttpHeaders headers; + + @Before + public void setup(){ + headers = new HttpHeaders(); + } @Test public void bitbucketSample() throws Exception { @@ -80,6 +87,24 @@ public class BitbucketPropertyPathNotificationExtractorTests { public void githubNotDetected() throws Exception { assertNotExtracted("github.json", "repo:push"); } + + @Test + public void missingUuidHeader() throws Exception { + // https://confluence.atlassian.com/bitbucket/event-payloads-740262817.html#EventPayloads-Push + Map value = readPayload("bitbucket.json"); + this.headers.set("X-Event-Key", "repo:push"); + PropertyPathNotification extracted = this.extractor.extract(this.headers, value); + assertNull(extracted); + } + + @Test + public void missingChanges() throws Exception { + // https://confluence.atlassian.com/bitbucket/event-payloads-740262817.html#EventPayloads-Push + Map value = readPayload("bitbucket-invalid.json"); + setHeaders("repo:push"); + PropertyPathNotification extracted = this.extractor.extract(this.headers, value); + assertNull(extracted); + } private void assertNotExtracted(String path, String eventKey) throws java.io.IOException { Map value = readPayload(path); @@ -87,7 +112,62 @@ public class BitbucketPropertyPathNotificationExtractorTests { PropertyPathNotification extracted = this.extractor.extract(this.headers, value); assertNull(extracted); } + + @Test + public void bitbucketServerSample() throws Exception { + // https://confluence.atlassian.com/bitbucketserver/event-payload-938025882.html + Map value = readPayload("bitbucketserver.json"); + setServerHeaders("repo:refs_changed"); + PropertyPathNotification extracted = this.extractor.extract(this.headers, value); + assertNotNull(extracted); + assertEquals("application.yml", extracted.getPaths()[0]); + } + + @Test + public void bitbucketServerSamplePullRequest() throws Exception { + // https://confluence.atlassian.com/bitbucketserver/event-payload-938025882.html + Map value = readPayload("bitbucketserver.json"); + setServerHeaders("pr:merged"); + PropertyPathNotification extracted = this.extractor.extract(this.headers, value); + assertNotNull(extracted); + assertEquals("application.yml", extracted.getPaths()[0]); + } + private void setServerHeaders(String eventKey) { + this.headers.set("X-Event-Key", eventKey); + this.headers.set("X-Request-Id", UUID.randomUUID().toString()); + } + + @Test + public void notAPushOrPullRequestServer() throws Exception { + assertNotExtractedServer("bitbucketserver.json", "repo:comment:added"); + } + + @Test + public void missingUuidHeaderServer() throws Exception { + // https://confluence.atlassian.com/bitbucketserver/event-payload-938025882.html + Map value = readPayload("bitbucketserver.json"); + this.headers.set("X-Event-Key", "repo:refs_changed"); + PropertyPathNotification extracted = this.extractor.extract(this.headers, value); + assertNull(extracted); + } + + @Test + public void missingChangesServer() throws Exception { + // https://confluence.atlassian.com/bitbucketserver/event-payload-938025882.html + Map value = readPayload("bitbucketserver-invalid.json"); + setServerHeaders("repo:refs_changed"); + PropertyPathNotification extracted = this.extractor.extract(this.headers, value); + assertNull(extracted); + } + + private void assertNotExtractedServer(String path, String eventKey) throws java.io.IOException { + Map value = readPayload(path); + setServerHeaders(eventKey); + PropertyPathNotification extracted = this.extractor.extract(this.headers, value); + assertNull(extracted); + } + private Map readPayload(String path) throws java.io.IOException { return new ObjectMapper().readValue( new ClassPathResource(path).getInputStream(), diff --git a/spring-cloud-config-monitor/src/test/resources/bitbucket-invalid.json b/spring-cloud-config-monitor/src/test/resources/bitbucket-invalid.json new file mode 100644 index 00000000..6ac0c92f --- /dev/null +++ b/spring-cloud-config-monitor/src/test/resources/bitbucket-invalid.json @@ -0,0 +1,38 @@ +{ + "actor": { + "username": "emmap1", + "display_name": "Emma", + "uuid": "{a54f16da-24e9-4d7f-a3a7-b1ba2cd98aa3}", + "links": { + "self": { + "href": "https://api.bitbucket.org/api/2.0/users/emmap1" + }, + "html": { + "href": "https://api.bitbucket.org/emmap1" + }, + "avatar": { + "href": "https://bitbucket-api-assetroot.s3.amazonaws.com/c/photos/2015/Feb/26/3613917261-0-emmap1-avatar_avatar.png" + } + } + }, + "repository": { + "links": { + "self": { + "href": "https://api.bitbucket.org/api/2.0/repositories/bitbucket/bitbucket" + }, + "html": { + "href": "https://api.bitbucket.org/bitbucket/bitbucket" + }, + "avatar": { + "href": "https://api-staging-assetroot.s3.amazonaws.com/c/photos/2014/Aug/01/bitbucket-logo-2629490769-3_avatar.png" + } + }, + "uuid": "{673a6070-3421-46c9-9d48-90745f7bfe8e}", + "full_name": "team_name/repo_name", + "name": "repo_name", + "scm": "git", + "is_private": true + }, + "push": { + } +} \ No newline at end of file diff --git a/spring-cloud-config-monitor/src/test/resources/bitbucketserver-invalid.json b/spring-cloud-config-monitor/src/test/resources/bitbucketserver-invalid.json new file mode 100644 index 00000000..26e3912c --- /dev/null +++ b/spring-cloud-config-monitor/src/test/resources/bitbucketserver-invalid.json @@ -0,0 +1,30 @@ +{ + "eventKey":"repo:refs_changed", + "date":"2017-09-19T09:45:32+1000", + "actor":{ + "name":"admin", + "emailAddress":"admin@example.com", + "id":1, + "displayName":"Administrator", + "active":true, + "slug":"admin", + "type":"NORMAL" + }, + "repository":{ + "slug":"repository", + "id":84, + "name":"repository", + "scmId":"git", + "state":"AVAILABLE", + "statusMessage":"Available", + "forkable":true, + "project":{ + "key":"PROJ", + "id":84, + "name":"project", + "public":false, + "type":"NORMAL" + }, + "public":false + } +} \ No newline at end of file diff --git a/spring-cloud-config-monitor/src/test/resources/bitbucketserver.json b/spring-cloud-config-monitor/src/test/resources/bitbucketserver.json new file mode 100644 index 00000000..b0d88522 --- /dev/null +++ b/spring-cloud-config-monitor/src/test/resources/bitbucketserver.json @@ -0,0 +1,43 @@ +{ + "eventKey":"repo:refs_changed", + "date":"2017-09-19T09:45:32+1000", + "actor":{ + "name":"admin", + "emailAddress":"admin@example.com", + "id":1, + "displayName":"Administrator", + "active":true, + "slug":"admin", + "type":"NORMAL" + }, + "repository":{ + "slug":"repository", + "id":84, + "name":"repository", + "scmId":"git", + "state":"AVAILABLE", + "statusMessage":"Available", + "forkable":true, + "project":{ + "key":"PROJ", + "id":84, + "name":"project", + "public":false, + "type":"NORMAL" + }, + "public":false + }, + "changes":[ + { + "ref":{ + "id":"refs/heads/master", + "displayId":"master", + "type":"BRANCH" + }, + "refId":"refs/heads/master", + "fromHash":"ecddabb624f6f5ba43816f5926e580a5f680a932", + "toHash":"178864a7d521b6f5e720b386b2c2b0ef8563e0dc", + "type":"UPDATE" + } + ] +} \ No newline at end of file