Repository: xanzy/go-gitlab
Pull request #1892 (2024-03-06 21:10:54)

Add support for the CustomWebhookTemplate
This PR adds support for the newly introduced `custom_webhook_template` attributes on Project and Group webhooks.

Note: While the documentation for this feature isn't quite published yet, it should be within a day or two. See the MR here: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/146823

(here's the release notes link too, in case you're curious: https://about.gitlab.com/releases/gitlab-com/#custom-payload-template-for-webhooks)


Patch:
diff --git a/group_hooks.go b/group_hooks.go
index ccfed12..56f2fae 100644
--- a/group_hooks.go
+++ b/group_hooks.go
@@ -46,6 +46,7 @@ type GroupHook struct {
 	EnableSSLVerification    bool       `json:"enable_ssl_verification"`
 	AlertStatus              string     `json:"alert_status"`
 	CreatedAt                *time.Time `json:"created_at"`
+	CustomWebhookTemplate    string     `json:"custom_webhook_template"`
 }
 
 // ListGroupHooksOptions represents the available ListGroupHooks() options.
@@ -122,6 +123,7 @@ type AddGroupHookOptions struct {
 	SubGroupEvents           *bool   `url:"subgroup_events,omitempty" json:"subgroup_events,omitempty"`
 	EnableSSLVerification    *bool   `url:"enable_ssl_verification,omitempty"  json:"enable_ssl_verification,omitempty"`
 	Token                    *string `url:"token,omitempty" json:"token,omitempty"`
+	CustomWebhookTemplate    *string `url:"custom_webhook_template,omitempty" json:"custom_webhook_template,omitempty"`
 }
 
 // AddGroupHook create a new group scoped webhook.
@@ -170,6 +172,7 @@ type EditGroupHookOptions struct {
 	SubGroupEvents           *bool   `url:"subgroup_events,omitempty" json:"subgroup_events,omitempty"`
 	EnableSSLVerification    *bool   `url:"enable_ssl_verification,omitempty" json:"enable_ssl_verification,omitempty"`
 	Token                    *string `url:"token,omitempty" json:"token,omitempty"`
+	CustomWebhookTemplate    *string `url:"custom_webhook_template,omitempty" json:"custom_webhook_template,omitempty"`
 }
 
 // EditGroupHook edits a hook for a specified group.
diff --git a/projects.go b/projects.go
index e34bd37..ee4765a 100644
--- a/projects.go
+++ b/projects.go
@@ -1217,6 +1217,7 @@ type ProjectHook struct {
 	ReleasesEvents           bool       `json:"releases_events"`
 	EnableSSLVerification    bool       `json:"enable_ssl_verification"`
 	CreatedAt                *time.Time `json:"created_at"`
+	CustomWebhookTemplate    string     `json:"custom_webhook_template"`
 }
 
 // ListProjectHooksOptions represents the available ListProjectHooks() options.
@@ -1295,6 +1296,7 @@ type AddProjectHookOptions struct {
 	Token                    *string `url:"token,omitempty" json:"token,omitempty"`
 	URL                      *string `url:"url,omitempty" json:"url,omitempty"`
 	WikiPageEvents           *bool   `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
+	CustomWebhookTemplate    *string `url:"custom_webhook_template,omitempty" json:"custom_webhook_template,omitempty"`
 }
 
 // AddProjectHook adds a hook to a specified project.
@@ -1343,6 +1345,7 @@ type EditProjectHookOptions struct {
 	Token                    *string `url:"token,omitempty" json:"token,omitempty"`
 	URL                      *string `url:"url,omitempty" json:"url,omitempty"`
 	WikiPageEvents           *bool   `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
+	CustomWebhookTemplate    *string `url:"custom_webhook_template,omitempty" json:"custom_webhook_template,omitempty"`
 }
 
 // EditProjectHook edits a hook for a specified project.


Test patch:
diff --git a/group_hooks_test.go b/group_hooks_test.go
index 1dde62a..20083e5 100644
--- a/group_hooks_test.go
+++ b/group_hooks_test.go
@@ -174,7 +174,8 @@ func TestAddGroupHook(t *testing.T) {
 	"releases_events": true,
 	"subgroup_events": true,
 	"enable_ssl_verification": true,
-	"created_at": "2012-10-12T17:04:47Z"
+	"created_at": "2012-10-12T17:04:47Z",
+	"custom_webhook_template": "addTestValue"
 }`)
 	})
 
@@ -209,6 +210,7 @@ func TestAddGroupHook(t *testing.T) {
 		SubGroupEvents:           true,
 		EnableSSLVerification:    true,
 		CreatedAt:                &datePointer,
+		CustomWebhookTemplate:    "addTestValue",
 	}
 
 	if !reflect.DeepEqual(groupHooks, want) {
@@ -240,7 +242,8 @@ func TestEditGroupHook(t *testing.T) {
 	"releases_events": true,
 	"subgroup_events": true,
 	"enable_ssl_verification": true,
-	"created_at": "2012-10-12T17:04:47Z"
+	"created_at": "2012-10-12T17:04:47Z",
+	"custom_webhook_template": "testValue"
 }`)
 	})
 
@@ -275,6 +278,7 @@ func TestEditGroupHook(t *testing.T) {
 		SubGroupEvents:           true,
 		EnableSSLVerification:    true,
 		CreatedAt:                &datePointer,
+		CustomWebhookTemplate:    "testValue",
 	}
 
 	if !reflect.DeepEqual(groupHooks, want) {
diff --git a/projects_test.go b/projects_test.go
index 4a5e4d5..2a2ffa4 100644
--- a/projects_test.go
+++ b/projects_test.go
@@ -1468,3 +1468,64 @@ func TestProjectModelsOptionalMergeAttribute(t *testing.T) {
 	}
 	assert.False(t, strings.Contains(string(jsonString), "only_allow_merge_if_all_status_checks_passed"))
 }
+
+// Test that the "CustomWebhookTemplate" serializes properly
+func TestProjectAddWebhook_CustomTemplate(t *testing.T) {
+	mux, client := setup(t)
+	customWebhookSet := false
+
+	mux.HandleFunc("/api/v4/projects/1/hooks",
+		func(w http.ResponseWriter, r *http.Request) {
+			testMethod(t, r, http.MethodPost)
+			w.WriteHeader(http.StatusCreated)
+
+			body, err := io.ReadAll(r.Body)
+			if err != nil {
+				t.Fatalf("Unable to read body properly. Error: %v", err)
+			}
+			customWebhookSet = strings.Contains(string(body), "custom_webhook_template")
+
+			fmt.Fprint(w, `{
+				"custom_webhook_template": "testValue"
+			}`)
+		},
+	)
+
+	hook, resp, err := client.Projects.AddProjectHook(1, &AddProjectHookOptions{
+		CustomWebhookTemplate: Ptr(`{"example":"{{object_kind}}"}`),
+	})
+
+	assert.NoError(t, err)
+	assert.Equal(t, http.StatusCreated, resp.StatusCode)
+	assert.Equal(t, true, customWebhookSet)
+	assert.Equal(t, "testValue", hook.CustomWebhookTemplate)
+}
+
+// Test that the "CustomWebhookTemplate" serializes properly when editing
+func TestProjectEditWebhook_CustomTemplate(t *testing.T) {
+	mux, client := setup(t)
+	customWebhookSet := false
+
+	mux.HandleFunc("/api/v4/projects/1/hooks/1",
+		func(w http.ResponseWriter, r *http.Request) {
+			testMethod(t, r, http.MethodPut)
+			w.WriteHeader(http.StatusOK)
+
+			body, err := io.ReadAll(r.Body)
+			if err != nil {
+				t.Fatalf("Unable to read body properly. Error: %v", err)
+			}
+			customWebhookSet = strings.Contains(string(body), "custom_webhook_template")
+
+			fmt.Fprint(w, "{}")
+		},
+	)
+
+	_, resp, err := client.Projects.EditProjectHook(1, 1, &EditProjectHookOptions{
+		CustomWebhookTemplate: Ptr(`{"example":"{{object_kind}}"}`),
+	})
+
+	assert.NoError(t, err)
+	assert.Equal(t, http.StatusOK, resp.StatusCode)
+	assert.Equal(t, true, customWebhookSet)
+}