From e4fe5885683e6d69323c731842a80282549e14ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Kantoj=C3=A4rvi?= Date: Tue, 23 Nov 2021 15:44:42 +0200 Subject: [PATCH] Implement pull-request related events in Stash driver * handle rebases to a pull request * handle target branch changes of a pull request * handle deletion of a pull request --- scm/driver/stash/pr.go | 10 + scm/driver/stash/testdata/pr.json.golden | 10 + scm/driver/stash/testdata/prs.json.golden | 10 + .../testdata/webhooks/pr_declined.json.golden | 10 + .../testdata/webhooks/pr_deleted.json.golden | 54 ++++- .../webhooks/pr_from_ref_updated.json | 159 +++++++++++++ .../webhooks/pr_from_ref_updated.json.golden | 53 +++++ .../testdata/webhooks/pr_merged.json.golden | 10 + .../testdata/webhooks/pr_modified_meta.json | 224 ++++++++++++++++++ .../webhooks/pr_modified_meta.json.golden | 53 +++++ .../testdata/webhooks/pr_modified_toref.json | 167 +++++++++++++ .../webhooks/pr_modified_toref.json.golden | 53 +++++ .../testdata/webhooks/pr_open.json.golden | 10 + scm/driver/stash/webhook.go | 30 ++- scm/driver/stash/webhook_test.go | 32 +++ 15 files changed, 882 insertions(+), 3 deletions(-) create mode 100644 scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json create mode 100644 scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json.golden create mode 100644 scm/driver/stash/testdata/webhooks/pr_modified_meta.json create mode 100644 scm/driver/stash/testdata/webhooks/pr_modified_meta.json.golden create mode 100644 scm/driver/stash/testdata/webhooks/pr_modified_toref.json create mode 100644 scm/driver/stash/testdata/webhooks/pr_modified_toref.json.golden diff --git a/scm/driver/stash/pr.go b/scm/driver/stash/pr.go index 061b27b3a..836c5392b 100644 --- a/scm/driver/stash/pr.go +++ b/scm/driver/stash/pr.go @@ -230,6 +230,16 @@ func convertPullRequest(from *pr) *scm.PullRequest { Merged: from.State == "MERGED", Created: time.Unix(from.CreatedDate/1000, 0), Updated: time.Unix(from.UpdatedDate/1000, 0), + Head: scm.Reference{ + Name: from.FromRef.DisplayID, + Path: from.FromRef.ID, + Sha: from.FromRef.LatestCommit, + }, + Base: scm.Reference{ + Name: from.ToRef.DisplayID, + Path: from.ToRef.ID, + Sha: from.ToRef.LatestCommit, + }, Author: scm.User{ Login: from.Author.User.Slug, Name: from.Author.User.DisplayName, diff --git a/scm/driver/stash/testdata/pr.json.golden b/scm/driver/stash/testdata/pr.json.golden index 8ec0d5d2f..72e48a411 100644 --- a/scm/driver/stash/testdata/pr.json.golden +++ b/scm/driver/stash/testdata/pr.json.golden @@ -10,6 +10,16 @@ "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/1", "Closed": false, "Merged": false, + "Base": { + "Name": "master", + "Path": "refs/heads/master", + "Sha": "5c64a07cd6c0f21b753bf261ef059c7e7633c50a" + }, + "Head": { + "Name": "feature/x", + "Path": "refs/heads/feature/x", + "Sha": "131cb13f4aed12e725177bc4b7c28db67839bf9f" + }, "Author": { "Login": "jcitizen", "Name": "Jane Citizen", diff --git a/scm/driver/stash/testdata/prs.json.golden b/scm/driver/stash/testdata/prs.json.golden index c9ede89d8..085a38788 100644 --- a/scm/driver/stash/testdata/prs.json.golden +++ b/scm/driver/stash/testdata/prs.json.golden @@ -11,6 +11,16 @@ "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/1", "Closed": false, "Merged": false, + "Base": { + "Name": "master", + "Path": "refs/heads/master", + "Sha": "5c64a07cd6c0f21b753bf261ef059c7e7633c50a" + }, + "Head": { + "Name": "feature/x", + "Path": "refs/heads/feature/x", + "Sha": "131cb13f4aed12e725177bc4b7c28db67839bf9f" + }, "Author": { "Login": "jcitizen", "Name": "Jane Citizen", diff --git a/scm/driver/stash/testdata/webhooks/pr_declined.json.golden b/scm/driver/stash/testdata/webhooks/pr_declined.json.golden index 828961b05..936b70c82 100644 --- a/scm/driver/stash/testdata/webhooks/pr_declined.json.golden +++ b/scm/driver/stash/testdata/webhooks/pr_declined.json.golden @@ -25,6 +25,16 @@ "Link": "", "Closed": true, "Merged": false, + "Base": { + "Name": "master", + "Path": "refs/heads/master", + "Sha": "823b2230a56056231c9425d63758fa87078a66b4" + }, + "Head": { + "Name": "develop", + "Path": "refs/heads/develop", + "Sha": "b9eaed50a03c073b20dfa82e5e753d295e7f0e56" + }, "Author": { "Login": "jcitizen", "Name": "Jane Citizen", diff --git a/scm/driver/stash/testdata/webhooks/pr_deleted.json.golden b/scm/driver/stash/testdata/webhooks/pr_deleted.json.golden index 9e26dfeeb..7852bd44a 100644 --- a/scm/driver/stash/testdata/webhooks/pr_deleted.json.golden +++ b/scm/driver/stash/testdata/webhooks/pr_deleted.json.golden @@ -1 +1,53 @@ -{} \ No newline at end of file +{ + "Action": "closed", + "Repo": { + "ID": "1", + "Namespace": "PRJ", + "Name": "my-repo", + "Perm": null, + "Branch": "master", + "Private": true, + "Clone": "", + "CloneSSH": "", + "Link": "", + "Created": "0001-01-01T00:00:00Z", + "Updated": "0001-01-01T00:00:00Z" + }, + "PullRequest": { + "Number": 2, + "Title": "added LICENSE", + "Body": "added BSD license text", + "Sha": "b9eaed50a03c073b20dfa82e5e753d295e7f0e56", + "Ref": "refs/pull-requests/2/from", + "Source": "develop", + "Target": "master", + "Fork": "PRJ/my-repo", + "Link": "", + "Closed": true, + "Merged": false, + "Base": { + "Name": "master", + "Path": "refs/heads/master", + "Sha": "823b2230a56056231c9425d63758fa87078a66b4" + }, + "Head": { + "Name": "develop", + "Path": "refs/heads/develop", + "Sha": "b9eaed50a03c073b20dfa82e5e753d295e7f0e56" + }, + "Author": { + "Login": "jcitizen", + "Name": "Jane Citizen", + "Email": "jane@example.com", + "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" + }, + "Created": "2018-07-05T12:21:30-07:00", + "Updated": "2018-07-05T12:30:48-07:00" + }, + "Sender": { + "Login": "jcitizen", + "Name": "Jane Citizen", + "Email": "jane@example.com", + "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" + } +} diff --git a/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json b/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json new file mode 100644 index 000000000..aaf938392 --- /dev/null +++ b/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json @@ -0,0 +1,159 @@ +{ + "eventKey": "pr:from_ref_updated", + "date": "2021-11-23T10:02:52+0000", + "actor": { + "name": "jcitizen", + "emailAddress": "jane@example.com", + "id": 1, + "displayName": "Jane Citizen", + "active": true, + "slug": "jcitizen", + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/users/jcitizen" + } + ] + } + }, + "pullRequest": { + "id": 2, + "version": 0, + "title": "added LICENSE", + "description": "added BSD license text", + "state": "OPEN", + "open": true, + "closed": false, + "createdDate": 1637591800463, + "updatedDate": 1637661772258, + "fromRef": { + "id": "refs/heads/feature-2", + "displayId": "feature-2", + "latestCommit": "c00d0f269a9844dd168f72f6a9d78a9cff49a318", + "type": "BRANCH", + "repository": { + "slug": "my-repo", + "id": 1, + "name": "my-repo", + "hierarchyId": "5aa23c1a1280b0c0acce", + "scmId": "git", + "state": "AVAILABLE", + "statusMessage": "Available", + "forkable": true, + "project": { + "key": "PRJ", + "id": 2, + "name": "PRJ", + "public": false, + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ" + } + ] + } + }, + "public": false, + "links": { + "clone": [ + { + "href": "http://example.com:7990/scm/prj/my-repo.git", + "name": "http" + }, + { + "href": "ssh://git@example.com:7999/prj/my-repo.git", + "name": "ssh" + } + ], + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse" + } + ] + } + } + }, + "toRef": { + "id": "refs/heads/master", + "displayId": "master", + "type": "BRANCH", + "latestCommit": "823b2230a56056231c9425d63758fa87078a66b4", + "repository": { + "slug": "my-repo", + "id": 1, + "name": "my-repo", + "hierarchyId": "5aa23c1a1280b0c0acce", + "scmId": "git", + "state": "AVAILABLE", + "statusMessage": "Available", + "forkable": true, + "project": { + "key": "PRJ", + "id": 2, + "name": "PRJ", + "public": false, + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ" + } + ] + } + }, + "public": false, + "links": { + "clone": [ + { + "href": "http://example.com:7990/scm/prj/my-repo.git", + "name": "http" + }, + { + "href": "ssh://git@example.com:7999/prj/my-repo.git", + "name": "ssh" + } + ], + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse" + } + ] + } + } + }, + "locked": false, + "author": { + "user": { + "name": "jcitizen", + "emailAddress": "jane@example.com", + "id": 1, + "displayName": "Jane Citizen", + "active": true, + "slug": "jcitizen", + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/users/jcitizen" + } + ] + } + }, + "role": "AUTHOR", + "approved": false, + "status": "UNAPPROVED" + }, + "reviewers": [], + "participants": [], + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/2" + } + ] + } + }, + "previousFromHash": "caf52ccb18c039cd9b930ed1f7b3edfb34cb92dd" +} diff --git a/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json.golden b/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json.golden new file mode 100644 index 000000000..ce48bb948 --- /dev/null +++ b/scm/driver/stash/testdata/webhooks/pr_from_ref_updated.json.golden @@ -0,0 +1,53 @@ +{ + "Action": "synchronized", + "Repo": { + "ID": "1", + "Namespace": "PRJ", + "Name": "my-repo", + "Perm": null, + "Branch": "master", + "Private": true, + "Clone": "http://example.com:7990/scm/prj/my-repo.git", + "CloneSSH": "ssh://git@example.com:7999/prj/my-repo.git", + "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/browse", + "Created": "0001-01-01T00:00:00Z", + "Updated": "0001-01-01T00:00:00Z" + }, + "PullRequest": { + "Number": 2, + "Title": "added LICENSE", + "Body": "added BSD license text", + "Sha": "c00d0f269a9844dd168f72f6a9d78a9cff49a318", + "Ref": "refs/pull-requests/2/from", + "Source": "feature-2", + "Target": "master", + "Fork": "PRJ/my-repo", + "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/2", + "Closed": false, + "Merged": false, + "Base": { + "Name": "master", + "Path": "refs/heads/master", + "Sha": "823b2230a56056231c9425d63758fa87078a66b4" + }, + "Head": { + "Name": "feature-2", + "Path": "refs/heads/feature-2", + "Sha": "c00d0f269a9844dd168f72f6a9d78a9cff49a318" + }, + "Author": { + "Login": "jcitizen", + "Name": "Jane Citizen", + "Email": "jane@example.com", + "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" + }, + "Created": "2021-11-22T14:36:40Z", + "Updated": "2021-11-23T10:02:52Z" + }, + "Sender": { + "Login": "jcitizen", + "Name": "Jane Citizen", + "Email": "jane@example.com", + "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" + } +} diff --git a/scm/driver/stash/testdata/webhooks/pr_merged.json.golden b/scm/driver/stash/testdata/webhooks/pr_merged.json.golden index 7f15cd419..344032d2c 100644 --- a/scm/driver/stash/testdata/webhooks/pr_merged.json.golden +++ b/scm/driver/stash/testdata/webhooks/pr_merged.json.golden @@ -25,6 +25,16 @@ "Link": "", "Closed": true, "Merged": true, + "Base": { + "Name": "master", + "Path": "refs/heads/master", + "Sha": "823b2230a56056231c9425d63758fa87078a66b4" + }, + "Head": { + "Name": "develop", + "Path": "refs/heads/develop", + "Sha": "b9eaed50a03c073b20dfa82e5e753d295e7f0e56" + }, "Author": { "Login": "jcitizen", "Name": "Jane Citizen", diff --git a/scm/driver/stash/testdata/webhooks/pr_modified_meta.json b/scm/driver/stash/testdata/webhooks/pr_modified_meta.json new file mode 100644 index 000000000..a44e432b7 --- /dev/null +++ b/scm/driver/stash/testdata/webhooks/pr_modified_meta.json @@ -0,0 +1,224 @@ +{ + "eventKey": "pr:modified", + "date": "2021-11-24T14:43:48+0000", + "actor": { + "name": "jcitizen", + "emailAddress": "jane@example.com", + "id": 1, + "displayName": "Jane Citizen", + "active": true, + "slug": "jcitizen", + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/users/jcitizen" + } + ] + } + }, + "pullRequest": { + "id": 6, + "version": 1, + "title": "Clean README.md", + "description": "Removed old information out of `REAME.nd`.", + "state": "OPEN", + "open": true, + "closed": false, + "createdDate": 1637764009216, + "updatedDate": 1637765028112, + "fromRef": { + "id": "refs/heads/cleanup", + "displayId": "cleanup", + "latestCommit": "a268fca7b65055e13f60ac1a2c9a527615f113ca", + "type": "BRANCH", + "repository": { + "slug": "my-repo", + "id": 12, + "name": "My Repo", + "hierarchyId": "5aa23c1a1280b0c0acce", + "scmId": "git", + "state": "AVAILABLE", + "statusMessage": "Available", + "forkable": true, + "origin": { + "slug": "my-repo", + "id": 1, + "name": "My Repo", + "hierarchyId": "5aa23c1a1280b0c0acce", + "scmId": "git", + "state": "AVAILABLE", + "statusMessage": "Available", + "forkable": true, + "project": { + "key": "PRJ", + "id": 2, + "name": "Project", + "public": false, + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ" + } + ] + } + }, + "public": false, + "links": { + "clone": [ + { + "href": "ssh://git@example.com:7999/prj/my-repo.git", + "name": "ssh" + }, + { + "href": "http://example.com:7990/scm/prj/my-repo.git", + "name": "http" + } + ], + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse" + } + ] + } + }, + "project": { + "key": "~jcitizen", + "id": 1, + "name": "Jane Citizen", + "type": "PERSONAL", + "owner": { + "name": "jcitizen", + "emailAddress": "jane@example.com", + "id": 2, + "displayName": "Jane Citizen", + "active": true, + "slug": "jcitizen", + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/users/jcitizen" + } + ] + } + }, + "links": { + "self": [ + { + "href": "http://example.com:7990/users/jcitizen" + } + ] + } + }, + "public": false, + "links": { + "clone": [ + { + "href": "http://example.com:7990/scm/~jcitizen/my-repo.git", + "name": "http" + }, + { + "href": "ssh://git@example.com:7999/~jcitizen/my-repo.git", + "name": "ssh" + } + ], + "self": [ + { + "href": "http://example.com:7990/users/jcitizen/repos/my-repo/browse" + } + ] + } + } + }, + "toRef": { + "id": "refs/heads/master", + "displayId": "master", + "latestCommit": "a4dae7792cf772d72149730091141ef48bba8697", + "type": "BRANCH", + "repository": { + "slug": "my-repo", + "id": 1, + "name": "My Repo", + "hierarchyId": "5aa23c1a1280b0c0acce", + "scmId": "git", + "state": "AVAILABLE", + "statusMessage": "Available", + "forkable": true, + "project": { + "key": "PRJ", + "id": 2, + "name": "Project", + "public": false, + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ" + } + ] + } + }, + "public": false, + "links": { + "clone": [ + { + "href": "ssh://git@example.com:7999/prj/my-repo.git", + "name": "ssh" + }, + { + "href": "http://example.com:7990/scm/prj/my-repo.git", + "name": "http" + } + ], + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse" + } + ] + } + } + }, + "locked": false, + "author": { + "user": { + "name": "jcitizen", + "emailAddress": "jane@example.com", + "id": 1, + "displayName": "Jane Citizen", + "active": true, + "slug": "jcitizen", + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/users/jcitizen" + } + ] + } + }, + "role": "AUTHOR", + "approved": false, + "status": "UNAPPROVED" + }, + "reviewers": [], + "participants": [], + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/6" + } + ] + } + }, + "previousTitle": "Clean README.md", + "previousDescription": "Cleaned old info out of REAME.", + "previousTarget": { + "id": "refs/heads/master", + "displayId": "master", + "type": "BRANCH", + "latestCommit": "a4dae7792cf772d72149730091141ef48bba8697", + "latestChangeset": "a4dae7792cf772d72149730091141ef48bba8697" + } +} diff --git a/scm/driver/stash/testdata/webhooks/pr_modified_meta.json.golden b/scm/driver/stash/testdata/webhooks/pr_modified_meta.json.golden new file mode 100644 index 000000000..d901de8cc --- /dev/null +++ b/scm/driver/stash/testdata/webhooks/pr_modified_meta.json.golden @@ -0,0 +1,53 @@ +{ + "Action": "updated", + "Repo": { + "ID": "1", + "Namespace": "PRJ", + "Name": "my-repo", + "Perm": null, + "Branch": "master", + "Private": true, + "Clone": "http://example.com:7990/scm/prj/my-repo.git", + "CloneSSH": "ssh://git@example.com:7999/prj/my-repo.git", + "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/browse", + "Created": "0001-01-01T00:00:00Z", + "Updated": "0001-01-01T00:00:00Z" + }, + "PullRequest": { + "Number": 6, + "Title": "Clean README.md", + "Body": "Removed old information out of `REAME.nd`.", + "Sha": "a268fca7b65055e13f60ac1a2c9a527615f113ca", + "Ref": "refs/pull-requests/6/from", + "Source": "cleanup", + "Target": "master", + "Fork": "~jcitizen/my-repo", + "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/6", + "Closed": false, + "Merged": false, + "Base": { + "Name": "master", + "Path": "refs/heads/master", + "Sha": "a4dae7792cf772d72149730091141ef48bba8697" + }, + "Head": { + "Name": "cleanup", + "Path": "refs/heads/cleanup", + "Sha": "a268fca7b65055e13f60ac1a2c9a527615f113ca" + }, + "Author": { + "Login": "jcitizen", + "Name": "Jane Citizen", + "Email": "jane@example.com", + "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" + }, + "Created": "2021-11-24T14:26:49Z", + "Updated": "2021-11-24T14:43:48Z" + }, + "Sender": { + "Login": "jcitizen", + "Name": "Jane Citizen", + "Email": "jane@example.com", + "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" + } +} diff --git a/scm/driver/stash/testdata/webhooks/pr_modified_toref.json b/scm/driver/stash/testdata/webhooks/pr_modified_toref.json new file mode 100644 index 000000000..ff33036c6 --- /dev/null +++ b/scm/driver/stash/testdata/webhooks/pr_modified_toref.json @@ -0,0 +1,167 @@ +{ + "eventKey": "pr:modified", + "date": "2021-11-23T13:16:20+0000", + "actor": { + "name": "jcitizen", + "emailAddress": "jane@example.com", + "id": 1, + "displayName": "Jane Citizen", + "active": true, + "slug": "jcitizen", + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/users/jcitizen" + } + ] + } + }, + "pullRequest": { + "id": 2, + "version": 0, + "title": "added LICENSE", + "description": "added BSD license text", + "state": "OPEN", + "open": true, + "closed": false, + "createdDate": 1637591800463, + "updatedDate": 1637673380977, + "fromRef": { + "id": "refs/heads/feature-2", + "displayId": "feature-2", + "latestCommit": "c00d0f269a9844dd168f72f6a9d78a9cff49a318", + "type": "BRANCH", + "repository": { + "slug": "my-repo", + "id": 1, + "name": "my-repo", + "hierarchyId": "5aa23c1a1280b0c0acce", + "scmId": "git", + "state": "AVAILABLE", + "statusMessage": "Available", + "forkable": true, + "project": { + "key": "PRJ", + "id": 2, + "name": "PRJ", + "public": false, + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ" + } + ] + } + }, + "public": false, + "links": { + "clone": [ + { + "href": "http://example.com:7990/scm/prj/my-repo.git", + "name": "http" + }, + { + "href": "ssh://git@example.com:7999/prj/my-repo.git", + "name": "ssh" + } + ], + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse" + } + ] + } + } + }, + "toRef": { + "id": "refs/heads/master", + "displayId": "master", + "latestCommit": "0667def9e377a8fa76ce67b6d4e53e21b246f030", + "type": "BRANCH", + "repository": { + "slug": "my-repo", + "id": 1, + "name": "my-repo", + "hierarchyId": "5aa23c1a1280b0c0acce", + "scmId": "git", + "state": "AVAILABLE", + "statusMessage": "Available", + "forkable": true, + "project": { + "key": "PRJ", + "id": 2, + "name": "PRJ", + "public": false, + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ" + } + ] + } + }, + "public": false, + "links": { + "clone": [ + { + "href": "http://example.com:7990/scm/prj/my-repo.git", + "name": "http" + }, + { + "href": "ssh://git@example.com:7999/prj/my-repo.git", + "name": "ssh" + } + ], + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/browse" + } + ] + } + } + }, + "locked": false, + "author": { + "user": { + "name": "jcitizen", + "emailAddress": "jane@example.com", + "id": 1, + "displayName": "Jane Citizen", + "active": true, + "slug": "jcitizen", + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://example.com:7990/users/jcitizen" + } + ] + } + }, + "role": "AUTHOR", + "approved": false, + "status": "UNAPPROVED" + }, + "reviewers": [], + "participants": [], + "links": { + "self": [ + { + "href": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/2" + } + ] + } + }, + "previousTitle": "old title", + "previousDescription": "old description", + "previousTarget": { + "id": "refs/heads/wrong", + "displayId": "wrong", + "type": "BRANCH", + "latestCommit": "2bbad782fd5453ded95fc5ce8b16839ff10615e0", + "latestChangeset": "2bbad782fd5453ded95fc5ce8b16839ff10615e0" + } +} diff --git a/scm/driver/stash/testdata/webhooks/pr_modified_toref.json.golden b/scm/driver/stash/testdata/webhooks/pr_modified_toref.json.golden new file mode 100644 index 000000000..c7d808d75 --- /dev/null +++ b/scm/driver/stash/testdata/webhooks/pr_modified_toref.json.golden @@ -0,0 +1,53 @@ +{ + "Action": "synchronized", + "Repo": { + "ID": "1", + "Namespace": "PRJ", + "Name": "my-repo", + "Perm": null, + "Branch": "master", + "Private": true, + "Clone": "http://example.com:7990/scm/prj/my-repo.git", + "CloneSSH": "ssh://git@example.com:7999/prj/my-repo.git", + "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/browse", + "Created": "0001-01-01T00:00:00Z", + "Updated": "0001-01-01T00:00:00Z" + }, + "PullRequest": { + "Number": 2, + "Title": "added LICENSE", + "Body": "added BSD license text", + "Sha": "c00d0f269a9844dd168f72f6a9d78a9cff49a318", + "Ref": "refs/pull-requests/2/from", + "Source": "feature-2", + "Target": "master", + "Fork": "PRJ/my-repo", + "Link": "http://example.com:7990/projects/PRJ/repos/my-repo/pull-requests/2", + "Closed": false, + "Merged": false, + "Base": { + "Name": "master", + "Path": "refs/heads/master", + "Sha": "0667def9e377a8fa76ce67b6d4e53e21b246f030" + }, + "Head": { + "Name": "feature-2", + "Path": "refs/heads/feature-2", + "Sha": "c00d0f269a9844dd168f72f6a9d78a9cff49a318" + }, + "Author": { + "Login": "jcitizen", + "Name": "Jane Citizen", + "Email": "jane@example.com", + "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" + }, + "Created": "2021-11-22T14:36:40Z", + "Updated": "2021-11-23T13:16:20Z" + }, + "Sender": { + "Login": "jcitizen", + "Name": "Jane Citizen", + "Email": "jane@example.com", + "Avatar": "https://www.gravatar.com/avatar/9e26471d35a78862c17e467d87cddedf.jpg" + } +} diff --git a/scm/driver/stash/testdata/webhooks/pr_open.json.golden b/scm/driver/stash/testdata/webhooks/pr_open.json.golden index 5d29c8f49..cfed1a2ed 100644 --- a/scm/driver/stash/testdata/webhooks/pr_open.json.golden +++ b/scm/driver/stash/testdata/webhooks/pr_open.json.golden @@ -25,6 +25,16 @@ "Link": "", "Closed": false, "Merged": false, + "Base": { + "Name": "master", + "Path": "refs/heads/master", + "Sha": "823b2230a56056231c9425d63758fa87078a66b4" + }, + "Head": { + "Name": "develop", + "Path": "refs/heads/develop", + "Sha": "208b0a5c05eddadad01f2aed8802fe0c3b3eaf5e" + }, "Author": { "Login": "jcitizen", "Name": "Jane Citizen", diff --git a/scm/driver/stash/webhook.go b/scm/driver/stash/webhook.go index 84ef85cb1..31c130638 100644 --- a/scm/driver/stash/webhook.go +++ b/scm/driver/stash/webhook.go @@ -21,7 +21,6 @@ import ( // TODO(bradrydzewski) push hook does not include repository git+http link // TODO(bradrydzewski) push hook does not include repository git+ssh link // TODO(bradrydzewski) push hook does not include repository html link -// TODO(bradrydzewski) missing pull request synchrnoized webhook. See https://jira.atlassian.com/browse/BSERV-10279 // TODO(bradrydzewski) pr hook does not include repository git+http link // TODO(bradrydzewski) pr hook does not include repository git+ssh link // TODO(bradrydzewski) pr hook does not include repository html link @@ -42,7 +41,7 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo switch req.Header.Get("X-Event-Key") { case "repo:refs_changed": hook, err = s.parsePushHook(data) - case "pr:opened", "pr:declined", "pr:merged": + case "pr:opened", "pr:from_ref_updated", "pr:modified", "pr:declined", "pr:deleted", "pr:merged": hook, err = s.parsePullRequest(data) } if err != nil { @@ -100,8 +99,25 @@ func (s *webhookService) parsePullRequest(data []byte) (scm.Webhook, error) { switch src.EventKey { case "pr:opened": dst.Action = scm.ActionOpen + case "pr:from_ref_updated": + // The request includes field "previousFromHash", which could be compared + // to the FromRef.Latestcommit to ensure there is actually a change, + // but there is unlikely need for that. + dst.Action = scm.ActionSync + case "pr:modified": + // BitBucket Server (Stash) sends "pr:modified" for any edits to the PR, + // including edits to the title or description. Thus, return the hook + // action only when the target reference has changed (name or hash). + if src.PullRequest.ToRef.DisplayID == src.PreviousTarget.DisplayID && + src.PullRequest.ToRef.LatestCommit == src.PreviousTarget.LatestCommit { + dst.Action = scm.ActionUpdate + } else { + dst.Action = scm.ActionSync + } case "pr:declined": dst.Action = scm.ActionClose + case "pr:deleted": + dst.Action = scm.ActionClose case "pr:merged": dst.Action = scm.ActionMerge default: @@ -127,6 +143,16 @@ type pullRequestHook struct { Date string `json:"date"` Actor *user `json:"actor"` PullRequest *pr `json:"pullRequest"` + // only in pr:from_ref_updated + PreviousFromHash string `json:"previousFromHash"` + // only in pr:modified + PreviousTarget struct { + ID string `json:"id"` // "refs/heads/master" + DisplayID string `json:"displayId"` // "master" + Type string `json:"type"` // "BRANCH" + LatestCommit string `json:"latestCommit"` // "860c4eb4ed0f969b47144234ba13c31c498cca69" + LatestChangeset string `json:"latestChangeset"` // "860c4eb4ed0f969b47144234ba13c31c498cca69" + } `json:"previousTarget"` } type change struct { diff --git a/scm/driver/stash/webhook_test.go b/scm/driver/stash/webhook_test.go index eb0486ef1..711adc694 100644 --- a/scm/driver/stash/webhook_test.go +++ b/scm/driver/stash/webhook_test.go @@ -92,6 +92,30 @@ func TestWebhooks(t *testing.T) { after: "testdata/webhooks/pr_open.json.golden", obj: new(scm.PullRequestHook), }, + // pull request source branch updated + { + sig: "71295b197fa25f4356d2fb9965df3f2379d903d7", + event: "pr:from_ref_updated", + before: "testdata/webhooks/pr_from_ref_updated.json", + after: "testdata/webhooks/pr_from_ref_updated.json.golden", + obj: new(scm.PullRequestHook), + }, + // pull request modified, target branch updated + { + sig: "71295b197fa25f4356d2fb9965df3f2379d903d7", + event: "pr:modified", + before: "testdata/webhooks/pr_modified_toref.json", + after: "testdata/webhooks/pr_modified_toref.json.golden", + obj: new(scm.PullRequestHook), + }, + // pull request modified, no change to toref + { + sig: "71295b197fa25f4356d2fb9965df3f2379d903d7", + event: "pr:modified", + before: "testdata/webhooks/pr_modified_meta.json", + after: "testdata/webhooks/pr_modified_meta.json.golden", + obj: new(scm.PullRequestHook), + }, // pull request fulfilled (merged) { sig: "71295b197fa25f4356d2fb9965df3f2379d903d7", @@ -108,6 +132,14 @@ func TestWebhooks(t *testing.T) { after: "testdata/webhooks/pr_declined.json.golden", obj: new(scm.PullRequestHook), }, + // pull request deleted + { + sig: "71295b197fa25f4356d2fb9965df3f2379d903d7", + event: "pr:deleted", + before: "testdata/webhooks/pr_deleted.json", + after: "testdata/webhooks/pr_deleted.json.golden", + obj: new(scm.PullRequestHook), + }, } for _, test := range tests {