Skip to content

Commit 06c5ede

Browse files
author
chhsia0
committed
Add support for updating a webhook in all SCMs.
The new `UpdateHook` method in `RepositoryService` would update an existing SCM webhook and overwrite the list of events to watch. In this patch, `UpdateHook` is not supported in the GitLab driver. It is implemented separately in a followup patch.
1 parent 565d225 commit 06c5ede

File tree

12 files changed

+309
-46
lines changed

12 files changed

+309
-46
lines changed

scm/driver/bitbucket/repo.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ func (s *repositoryService) CreateHook(ctx context.Context, repo string, input *
141141
in.Description = input.Name
142142
in.Events = append(
143143
input.NativeEvents,
144-
convertHookEvents(input.Events)...,
144+
convertFromHookEvents(input.Events)...,
145145
)
146146
out := new(hook)
147147
res, err := s.client.do(ctx, "POST", path, in, out)
@@ -163,6 +163,31 @@ func (s *repositoryService) CreateStatus(ctx context.Context, repo, ref string,
163163
return convertStatus(out), res, err
164164
}
165165

166+
// UpdateHook updates a repository webhook.
167+
func (s *repositoryService) UpdateHook(ctx context.Context, repo, id string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
168+
target, err := url.Parse(input.Target)
169+
if err != nil {
170+
return nil, nil, err
171+
}
172+
params := target.Query()
173+
params.Set("secret", input.Secret)
174+
target.RawQuery = params.Encode()
175+
176+
path := fmt.Sprintf("2.0/repositories/%s/hooks/%s", repo, id)
177+
in := new(hookInput)
178+
in.URL = target.String()
179+
in.SkipCertVerification = input.SkipVerify
180+
in.Active = true
181+
in.Description = input.Name
182+
in.Events = append(
183+
input.NativeEvents,
184+
convertFromHookEvents(input.Events)...,
185+
)
186+
out := new(hook)
187+
res, err := s.client.do(ctx, "PUT", path, in, out)
188+
return convertHook(out), res, err
189+
}
190+
166191
// DeleteHook deletes a repository webhook.
167192
func (s *repositoryService) DeleteHook(ctx context.Context, repo string, id string) (*scm.Response, error) {
168193
path := fmt.Sprintf("2.0/repositories/%s/hooks/%s", repo, id)
@@ -255,7 +280,7 @@ func convertHook(from *hook) *scm.Hook {
255280
}
256281
}
257282

258-
func convertHookEvents(from scm.HookEvents) []string {
283+
func convertFromHookEvents(from scm.HookEvents) []string {
259284
var events []string
260285
if from.Push {
261286
events = append(events, "repo:push")

scm/driver/bitbucket/repo_test.go

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -254,31 +254,43 @@ func TestRepositoryHookList(t *testing.T) {
254254
}
255255
}
256256

257-
func TestRepositoryHookDelete(t *testing.T) {
257+
func TestRepositoryHookCreate(t *testing.T) {
258258
defer gock.Off()
259259

260260
gock.New("https://api.bitbucket.org").
261-
Delete("/2.0/repositories/atlassian/stash-example-plugin/hooks/{d53603cc-3f67-45ea-b310-aaa5ef6ec061}").
262-
Reply(204).Done()
261+
Post("/2.0/repositories/atlassian/stash-example-plugin/hooks").
262+
Reply(201).
263+
Type("application/json").
264+
File("testdata/hook.json")
263265

264266
client, _ := New("https://api.bitbucket.org")
265-
_, err := client.Repositories.DeleteHook(context.Background(), "atlassian/stash-example-plugin", "{d53603cc-3f67-45ea-b310-aaa5ef6ec061}")
267+
got, _, err := client.Repositories.CreateHook(context.Background(), "atlassian/stash-example-plugin", &scm.HookInput{})
266268
if err != nil {
267269
t.Error(err)
270+
return
271+
}
272+
273+
want := new(scm.Hook)
274+
raw, _ := ioutil.ReadFile("testdata/hook.json.golden")
275+
json.Unmarshal(raw, want)
276+
277+
if diff := cmp.Diff(got, want); diff != "" {
278+
t.Errorf("Unexpected Results")
279+
t.Log(diff)
268280
}
269281
}
270282

271-
func TestRepositoryHookCreate(t *testing.T) {
283+
func TestRepositoryHookUpdate(t *testing.T) {
272284
defer gock.Off()
273285

274286
gock.New("https://api.bitbucket.org").
275-
Post("/2.0/repositories/atlassian/stash-example-plugin/hooks").
276-
Reply(201).
287+
Put("/2.0/repositories/atlassian/stash-example-plugin/hooks/{d53603cc-3f67-45ea-b310-aaa5ef6ec061}").
288+
Reply(200).
277289
Type("application/json").
278290
File("testdata/hook.json")
279291

280292
client, _ := New("https://api.bitbucket.org")
281-
got, _, err := client.Repositories.CreateHook(context.Background(), "atlassian/stash-example-plugin", &scm.HookInput{})
293+
got, _, err := client.Repositories.UpdateHook(context.Background(), "atlassian/stash-example-plugin", "{d53603cc-3f67-45ea-b310-aaa5ef6ec061}", &scm.HookInput{})
282294
if err != nil {
283295
t.Error(err)
284296
return
@@ -294,6 +306,20 @@ func TestRepositoryHookCreate(t *testing.T) {
294306
}
295307
}
296308

309+
func TestRepositoryHookDelete(t *testing.T) {
310+
defer gock.Off()
311+
312+
gock.New("https://api.bitbucket.org").
313+
Delete("/2.0/repositories/atlassian/stash-example-plugin/hooks/{d53603cc-3f67-45ea-b310-aaa5ef6ec061}").
314+
Reply(204).Done()
315+
316+
client, _ := New("https://api.bitbucket.org")
317+
_, err := client.Repositories.DeleteHook(context.Background(), "atlassian/stash-example-plugin", "{d53603cc-3f67-45ea-b310-aaa5ef6ec061}")
318+
if err != nil {
319+
t.Error(err)
320+
}
321+
}
322+
297323
func TestConvertFromState(t *testing.T) {
298324
tests := []struct {
299325
src scm.State

scm/driver/gitea/repo.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,31 @@ func (s *repositoryService) CreateStatus(ctx context.Context, repo string, ref s
9898
return convertStatus(out), res, err
9999
}
100100

101+
func (s *repositoryService) UpdateHook(ctx context.Context, repo, id string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
102+
target, err := url.Parse(input.Target)
103+
if err != nil {
104+
return nil, nil, err
105+
}
106+
params := target.Query()
107+
params.Set("secret", input.Secret)
108+
target.RawQuery = params.Encode()
109+
110+
path := fmt.Sprintf("api/v1/repos/%s/hooks/%s", repo, id)
111+
in := new(hook)
112+
in.Type = "gitea"
113+
in.Active = true
114+
in.Config.Secret = input.Secret
115+
in.Config.ContentType = "json"
116+
in.Config.URL = target.String()
117+
in.Events = append(
118+
input.NativeEvents,
119+
convertHookEvent(input.Events)...,
120+
)
121+
out := new(hook)
122+
res, err := s.client.do(ctx, "PATCH", path, in, out)
123+
return convertHook(out), res, err
124+
}
125+
101126
func (s *repositoryService) DeleteHook(ctx context.Context, repo string, id string) (*scm.Response, error) {
102127
path := fmt.Sprintf("api/v1/repos/%s/hooks/%s", repo, id)
103128
return s.client.do(ctx, "DELETE", path, nil, nil)

scm/driver/gitea/repo_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,31 @@ func TestHookCreate(t *testing.T) {
190190
}
191191
}
192192

193+
func TestHookUpdate(t *testing.T) {
194+
defer gock.Off()
195+
196+
gock.New("https://try.gitea.io").
197+
Patch("/api/v1/repos/go-gitea/gitea/hooks/20").
198+
Reply(200).
199+
Type("application/json").
200+
File("testdata/hook.json")
201+
202+
client, _ := New("https://try.gitea.io")
203+
got, _, err := client.Repositories.UpdateHook(context.Background(), "go-gitea/gitea", "20", &scm.HookInput{})
204+
if err != nil {
205+
t.Error(err)
206+
}
207+
208+
want := new(scm.Hook)
209+
raw, _ := ioutil.ReadFile("testdata/hook.json.golden")
210+
json.Unmarshal(raw, &want)
211+
212+
if diff := cmp.Diff(got, want); diff != "" {
213+
t.Errorf("Unexpected Results")
214+
t.Log(diff)
215+
}
216+
}
217+
193218
func TestHookDelete(t *testing.T) {
194219
defer gock.Off()
195220

scm/driver/github/repo.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ func (s *RepositoryService) CreateHook(ctx context.Context, repo string, input *
118118
}
119119
in.Events = append(
120120
input.NativeEvents,
121-
convertHookEvents(input.Events)...,
121+
convertFromHookEvents(input.Events)...,
122122
)
123123
out := new(hook)
124124
res, err := s.client.do(ctx, "POST", path, in, out)
@@ -154,8 +154,28 @@ func (s *RepositoryService) CreateDeployStatus(ctx context.Context, repo string,
154154
return convertDeployStatus(out), res, err
155155
}
156156

157+
// UpdateHook updates a repository webhook.
158+
func (s *RepositoryService) UpdateHook(ctx context.Context, repo, id string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
159+
path := fmt.Sprintf("repos/%s/hooks/%s", repo, id)
160+
in := new(hook)
161+
in.Active = true
162+
in.Config.Secret = input.Secret
163+
in.Config.ContentType = "json"
164+
in.Config.URL = input.Target
165+
if input.SkipVerify {
166+
in.Config.InsecureSSL = "1"
167+
}
168+
in.Events = append(
169+
input.NativeEvents,
170+
convertFromHookEvents(input.Events)...,
171+
)
172+
out := new(hook)
173+
res, err := s.client.do(ctx, "PATCH", path, in, out)
174+
return convertHook(out), res, err
175+
}
176+
157177
// DeleteHook deletes a repository webhook.
158-
func (s *RepositoryService) DeleteHook(ctx context.Context, repo string, id string) (*scm.Response, error) {
178+
func (s *RepositoryService) DeleteHook(ctx context.Context, repo, id string) (*scm.Response, error) {
159179
path := fmt.Sprintf("repos/%s/hooks/%s", repo, id)
160180
return s.client.do(ctx, "DELETE", path, nil, nil)
161181
}
@@ -210,7 +230,7 @@ func convertHook(from *hook) *scm.Hook {
210230
}
211231
}
212232

213-
func convertHookEvents(from scm.HookEvents) []string {
233+
func convertFromHookEvents(from scm.HookEvents) []string {
214234
var events []string
215235
if from.Push {
216236
events = append(events, "push")

scm/driver/github/repo_test.go

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -304,36 +304,49 @@ func TestRepositoryHookList(t *testing.T) {
304304
t.Run("Page", testPage(res))
305305
}
306306

307-
func TestRepositoryHookDelete(t *testing.T) {
307+
func TestRepositoryHookCreate(t *testing.T) {
308308
defer gock.Off()
309309

310310
gock.New("https://api.github.com").
311-
Delete("/repos/octocat/hello-world/hooks/1").
312-
Reply(204).
311+
Post("/repos/octocat/hello-world/hooks").
312+
Reply(201).
313313
Type("application/json").
314-
SetHeaders(mockHeaders)
314+
SetHeaders(mockHeaders).
315+
File("testdata/hook.json")
316+
317+
in := &scm.HookInput{
318+
Name: "drone",
319+
Target: "https://example.com",
320+
Secret: "topsecret",
321+
SkipVerify: true,
322+
}
315323

316324
client := NewDefault()
317-
res, err := client.Repositories.DeleteHook(context.Background(), "octocat/hello-world", "1")
325+
got, res, err := client.Repositories.CreateHook(context.Background(), "octocat/hello-world", in)
318326
if err != nil {
319327
t.Error(err)
320328
return
321329
}
322330

323-
if got, want := res.Status, 204; got != want {
324-
t.Errorf("Want response status %d, got %d", want, got)
331+
want := new(scm.Hook)
332+
raw, _ := ioutil.ReadFile("testdata/hook.json.golden")
333+
json.Unmarshal(raw, want)
334+
335+
if diff := cmp.Diff(got, want); diff != "" {
336+
t.Errorf("Unexpected Results")
337+
t.Log(diff)
325338
}
326339

327340
t.Run("Request", testRequest(res))
328341
t.Run("Rate", testRate(res))
329342
}
330343

331-
func TestRepositoryHookCreate(t *testing.T) {
344+
func TestRepositoryHookUpdate(t *testing.T) {
332345
defer gock.Off()
333346

334347
gock.New("https://api.github.com").
335-
Post("/repos/octocat/hello-world/hooks").
336-
Reply(201).
348+
Patch("/repos/octocat/hello-world/hooks/1").
349+
Reply(200).
337350
Type("application/json").
338351
SetHeaders(mockHeaders).
339352
File("testdata/hook.json")
@@ -346,7 +359,7 @@ func TestRepositoryHookCreate(t *testing.T) {
346359
}
347360

348361
client := NewDefault()
349-
got, res, err := client.Repositories.CreateHook(context.Background(), "octocat/hello-world", in)
362+
got, res, err := client.Repositories.UpdateHook(context.Background(), "octocat/hello-world", "1", in)
350363
if err != nil {
351364
t.Error(err)
352365
return
@@ -365,6 +378,30 @@ func TestRepositoryHookCreate(t *testing.T) {
365378
t.Run("Rate", testRate(res))
366379
}
367380

381+
func TestRepositoryHookDelete(t *testing.T) {
382+
defer gock.Off()
383+
384+
gock.New("https://api.github.com").
385+
Delete("/repos/octocat/hello-world/hooks/1").
386+
Reply(204).
387+
Type("application/json").
388+
SetHeaders(mockHeaders)
389+
390+
client := NewDefault()
391+
res, err := client.Repositories.DeleteHook(context.Background(), "octocat/hello-world", "1")
392+
if err != nil {
393+
t.Error(err)
394+
return
395+
}
396+
397+
if got, want := res.Status, 204; got != want {
398+
t.Errorf("Want response status %d, got %d", want, got)
399+
}
400+
401+
t.Run("Request", testRequest(res))
402+
t.Run("Rate", testRate(res))
403+
}
404+
368405
func TestConvertState(t *testing.T) {
369406
tests := []struct {
370407
src string
@@ -484,7 +521,7 @@ func TestHookEvents(t *testing.T) {
484521
},
485522
}
486523
for i, test := range tests {
487-
got, want := convertHookEvents(test.in), test.out
524+
got, want := convertFromHookEvents(test.in), test.out
488525
if diff := cmp.Diff(got, want); diff != "" {
489526
t.Errorf("Unexpected Results at index %d", i)
490527
t.Log(diff)

scm/driver/gitlab/repo.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ func (s *repositoryService) CreateStatus(ctx context.Context, repo, ref string,
153153
return convertStatus(out), res, err
154154
}
155155

156+
func (s *repositoryService) UpdateHook(ctx context.Context, repo string, id string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
157+
return nil, nil, scm.ErrNotSupported
158+
}
159+
156160
func (s *repositoryService) DeleteHook(ctx context.Context, repo string, id string) (*scm.Response, error) {
157161
path := fmt.Sprintf("api/v4/projects/%s/hooks/%s", encode(repo), id)
158162
return s.client.do(ctx, "DELETE", path, nil, nil)

scm/driver/gogs/repo.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,23 @@ func (s *repositoryService) CreateStatus(context.Context, string, string, *scm.S
7777
return nil, nil, scm.ErrNotSupported
7878
}
7979

80+
func (s *repositoryService) UpdateHook(ctx context.Context, repo, id string, input *scm.HookInput) (*scm.Hook, *scm.Response, error) {
81+
path := fmt.Sprintf("api/v1/repos/%s/hooks/%s", repo, id)
82+
in := new(hook)
83+
in.Type = "gogs"
84+
in.Active = true
85+
in.Config.Secret = input.Secret
86+
in.Config.ContentType = "json"
87+
in.Config.URL = input.Target
88+
in.Events = append(
89+
input.NativeEvents,
90+
convertHookEvent(input.Events)...,
91+
)
92+
out := new(hook)
93+
res, err := s.client.do(ctx, "PATCH", path, in, out)
94+
return convertHook(out), res, err
95+
}
96+
8097
func (s *repositoryService) DeleteHook(ctx context.Context, repo string, id string) (*scm.Response, error) {
8198
path := fmt.Sprintf("api/v1/repos/%s/hooks/%s", repo, id)
8299
return s.client.do(ctx, "DELETE", path, nil, nil)

0 commit comments

Comments
 (0)