Repository: xanzy/go-gitlab
Pull request #1802 (2023-09-24 03:29:05)

Add DataDog Service Support
This PR adds support to query, provision and delete [DataDog integrations](https://docs.gitlab.com/ee/integration/datadog.html) on a project or group basis. This utilizes the publicly documented [GitLab APIs](https://docs.gitlab.com/ee/integration/datadog.html). I have manually tested this on a self hosted instance and it appears to be working well.

The plan is to integrate this into the [GitLab Terraform Provider](https://gitlab.com/gitlab-org/terraform-provider-gitlab) assuming that I am able to get this code merged and cut into a release. Please let me know if there are any questions, concerns, or changes you'd like to see introduced.


Patch:
diff --git a/examples/services.go b/examples/services.go
new file mode 100644
index 0000000..3e48fb4
--- /dev/null
+++ b/examples/services.go
@@ -0,0 +1,63 @@
+//
+// Copyright 2023, Joel Gerber
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package main
+
+import (
+	"fmt"
+	"log"
+
+	"github.com/xanzy/go-gitlab"
+)
+
+func dataDogExample() {
+	git, err := gitlab.NewClient("yourtokengoeshere")
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Create new DataDog integration
+	opts := &gitlab.SetDataDogServiceOptions{
+		APIKey:             gitlab.String("testing"),
+		ArchiveTraceEvents: gitlab.Bool(true),
+		DataDogEnv:         gitlab.String("sandbox"),
+		DataDogService:     gitlab.String("test"),
+		DataDogSite:        gitlab.String("datadoghq.com"),
+		DataDogTags:        gitlab.String("country:canada\nprovince:ontario"),
+	}
+
+	_, err = git.Services.SetDataDogService(1, opts)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	// Query the integration
+	svc, _, err := git.Services.GetDataDogService(1)
+	if err != nil {
+		log.Fatal(err)
+	}
+	fmt.Printf(
+		"api_url: %s, archive_trace_events: %v, datadog_env: %s, datadog_service: %s, datadog_site: %s, datadog_tags: %s",
+		svc.Properties.APIURL, svc.Properties.ArchiveTraceEvents, svc.Properties.DataDogEnv,
+		svc.Properties.DataDogService, svc.Properties.DataDogSite, svc.Properties.DataDogTags,
+	)
+
+	// Delete the integration
+	_, err = git.Services.DeleteDataDogService(1)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/services.go b/services.go
index 69842ae..9ffdc45 100644
--- a/services.go
+++ b/services.go
@@ -179,6 +179,106 @@ func (s *ServicesService) DeleteCustomIssueTrackerService(pid interface{}, optio
 	return s.client.Do(req, nil)
 }
 
+// DataDogService represents DataDog service settings.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/services.html#datadog
+type DataDogService struct {
+	Service
+	Properties *DataDogServiceProperties `json:"properties"`
+}
+
+// DataDogServiceProperties represents DataDog specific properties.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/services.html#datadog
+type DataDogServiceProperties struct {
+	APIURL             string `url:"api_url,omitempty" json:"api_url,omitempty"`
+	ArchiveTraceEvents bool   `url:"archive_trace_events,omitempty" json:"archive_trace_events,omitempty"`
+	DataDogEnv         string `url:"datadog_env,omitempty" json:"datadog_env,omitempty"`
+	DataDogService     string `url:"datadog_service,omitempty" json:"datadog_service,omitempty"`
+	DataDogSite        string `url:"datadog_site,omitempty" json:"datadog_site,omitempty"`
+	DataDogTags        string `url:"datadog_tags,omitempty" json:"datadog_tags,omitempty"`
+}
+
+// GetDataDogService gets DataDog service settings for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/services.html#get-datadog-integration-settings
+func (s *ServicesService) GetDataDogService(pid interface{}, options ...RequestOptionFunc) (*DataDogService, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/datadog", PathEscape(project))
+
+	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	svc := new(DataDogService)
+	resp, err := s.client.Do(req, svc)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return svc, resp, nil
+}
+
+// SetDataDogServiceOptions represents the available SetDataDogService()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/services.html#createedit-datadog-integration
+type SetDataDogServiceOptions struct {
+	APIKey             *string `url:"api_key,omitempty" json:"api_key,omitempty"`
+	APIURL             *string `url:"api_url,omitempty" json:"api_url,omitempty"`
+	ArchiveTraceEvents *bool   `url:"archive_trace_events,omitempty" json:"archive_trace_events,omitempty"`
+	DataDogEnv         *string `url:"datadog_env,omitempty" json:"datadog_env,omitempty"`
+	DataDogService     *string `url:"datadog_service,omitempty" json:"datadog_service,omitempty"`
+	DataDogSite        *string `url:"datadog_site,omitempty" json:"datadog_site,omitempty"`
+	DataDogTags        *string `url:"datadog_tags,omitempty" json:"datadog_tags,omitempty"`
+}
+
+// SetDataDogService sets DataDog service settings for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/services.html#createedit-datadog-integration
+func (s *ServicesService) SetDataDogService(pid interface{}, opt *SetDataDogServiceOptions, options ...RequestOptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/datadog", PathEscape(project))
+
+	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteDataDogService deletes the DataDog service settings for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/services.html#disable-datadog-integration
+func (s *ServicesService) DeleteDataDogService(pid interface{}, options ...RequestOptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/datadog", PathEscape(project))
+
+	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
 // DiscordService represents Discord service settings.
 //
 // GitLab API docs:


Test patch:
diff --git a/services_test.go b/services_test.go
index d2452f9..e2b6836 100644
--- a/services_test.go
+++ b/services_test.go
@@ -105,6 +105,71 @@ func TestDeleteCustomIssueTrackerService(t *testing.T) {
 	}
 }
 
+func TestGetDataDogService(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/services/datadog", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, `{"id": 1, "active": true, "properties": {"datadog_site":"datadoghq.com", "api_url": "", "archive_trace_events": true, "datadog_service": "gitlab", "datadog_env": "production", "datadog_tags": "country=canada\nprovince=ontario"}}`)
+	})
+	want := &DataDogService{
+		Service: Service{ID: 1, Active: true},
+		Properties: &DataDogServiceProperties{
+			APIURL:             "",
+			ArchiveTraceEvents: true,
+			DataDogEnv:         "production",
+			DataDogService:     "gitlab",
+			DataDogSite:        "datadoghq.com",
+			DataDogTags:        "country=canada\nprovince=ontario",
+		},
+	}
+
+	service, _, err := client.Services.GetDataDogService(1)
+	if err != nil {
+		t.Fatalf("Services.GetDataDogService returns an error: %v", err)
+	}
+	if !reflect.DeepEqual(want, service) {
+		t.Errorf("Services.GetDataDogService returned %+v, want %+v", service, want)
+	}
+}
+
+func TestSetDataDogService(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/services/datadog", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPut)
+		testBody(t, r, `{"api_key":"secret","api_url":"https://some-api.com","archive_trace_events":false,"datadog_env":"sandbox","datadog_service":"source-code","datadog_site":"datadoghq.eu","datadog_tags":"country=france"}`)
+	})
+
+	opt := &SetDataDogServiceOptions{
+		APIKey:             String("secret"),
+		APIURL:             String("https://some-api.com"),
+		ArchiveTraceEvents: Bool(false),
+		DataDogEnv:         String("sandbox"),
+		DataDogService:     String("source-code"),
+		DataDogSite:        String("datadoghq.eu"),
+		DataDogTags:        String("country=france"),
+	}
+
+	_, err := client.Services.SetDataDogService(1, opt)
+	if err != nil {
+		t.Fatalf("Services.SetDataDogService returns an error: %v", err)
+	}
+}
+
+func TestDeleteDataDogService(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/services/datadog", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodDelete)
+	})
+
+	_, err := client.Services.DeleteDataDogService(1)
+	if err != nil {
+		t.Fatalf("Services.DeleteDataDogService returns an error: %v", err)
+	}
+}
+
 func TestGetDiscordService(t *testing.T) {
 	mux, client := setup(t)