Repository: xanzy/go-gitlab
Pull request #2058 (2024-11-09 18:07:23)

Add support for the project markdown uploads API
Fixes https://github.com/xanzy/go-gitlab/issues/2044

I wasn't entirely sure on how to do the upload function so I modelled it off generic_packages.go. I did find the gitlab.UploadRequest function later on though - should it use this instead?


Patch:
diff --git a/deploy_keys.go b/deploy_keys.go
index 8834edb..e343bef 100644
--- a/deploy_keys.go
+++ b/deploy_keys.go
@@ -67,7 +67,6 @@ type ProjectDeployKey struct {
 	Key       string     `json:"key"`
 	CreatedAt *time.Time `json:"created_at"`
 	CanPush   bool       `json:"can_push"`
-	ExpiresAt *time.Time `json:"expires_at"`
 }
 
 func (k ProjectDeployKey) String() string {
@@ -163,12 +162,11 @@ func (s *DeployKeysService) GetDeployKey(pid interface{}, deployKey int, options
 // AddDeployKeyOptions represents the available ADDDeployKey() options.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/deploy_keys.html#add-deploy-key-for-a-project
+// https://docs.gitlab.com/ee/api/deploy_keys.html#add-deploy-key
 type AddDeployKeyOptions struct {
-	Key       *string    `url:"key,omitempty" json:"key,omitempty"`
-	Title     *string    `url:"title,omitempty" json:"title,omitempty"`
-	CanPush   *bool      `url:"can_push,omitempty" json:"can_push,omitempty"`
-	ExpiresAt *time.Time `url:"expires_at,omitempty" json:"expires_at,omitempty"`
+	Title   *string `url:"title,omitempty" json:"title,omitempty"`
+	Key     *string `url:"key,omitempty" json:"key,omitempty"`
+	CanPush *bool   `url:"can_push,omitempty" json:"can_push,omitempty"`
 }
 
 // AddDeployKey creates a new deploy key for a project. If deploy key already
@@ -176,7 +174,7 @@ type AddDeployKeyOptions struct {
 // original one was is accessible by same user.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/deploy_keys.html#add-deploy-key-for-a-project
+// https://docs.gitlab.com/ee/api/deploy_keys.html#add-deploy-key
 func (s *DeployKeysService) AddDeployKey(pid interface{}, opt *AddDeployKeyOptions, options ...RequestOptionFunc) (*ProjectDeployKey, *Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
diff --git a/examples/services.go b/examples/services.go
index 16e88f1..029398b 100644
--- a/examples/services.go
+++ b/examples/services.go
@@ -39,11 +39,16 @@ func dataDogExample() {
 		ArchiveTraceEvents: gitlab.Bool(true),
 	}
 
-	svc, _, err := git.Services.SetDataDogService(1, opts)
+	_, 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, datadog_env: %s, datadog_service: %s, datadog_site: %s, datadog_tags: %s",
 		svc.Properties.APIURL, svc.Properties.DataDogEnv, svc.Properties.DataDogService,
diff --git a/gitlab.go b/gitlab.go
index 19ed3ea..be3e805 100644
--- a/gitlab.go
+++ b/gitlab.go
@@ -195,6 +195,7 @@ type Client struct {
 	ProjectFeatureFlags          *ProjectFeatureFlagService
 	ProjectImportExport          *ProjectImportExportService
 	ProjectIterations            *ProjectIterationsService
+	ProjectMarkdownUploads       *ProjectMarkdownUploadsService
 	ProjectMembers               *ProjectMembersService
 	ProjectMirrors               *ProjectMirrorService
 	ProjectRepositoryStorageMove *ProjectRepositoryStorageMoveService
@@ -433,6 +434,7 @@ func newClient(options ...ClientOptionFunc) (*Client, error) {
 	c.ProjectFeatureFlags = &ProjectFeatureFlagService{client: c}
 	c.ProjectImportExport = &ProjectImportExportService{client: c}
 	c.ProjectIterations = &ProjectIterationsService{client: c}
+	c.ProjectMarkdownUploads = &ProjectMarkdownUploadsService{client: c}
 	c.ProjectMembers = &ProjectMembersService{client: c}
 	c.ProjectMirrors = &ProjectMirrorService{client: c}
 	c.ProjectRepositoryStorageMove = &ProjectRepositoryStorageMoveService{client: c}
diff --git a/pages.go b/pages.go
index 7b0f503..81be8ba 100644
--- a/pages.go
+++ b/pages.go
@@ -90,38 +90,3 @@ func (s *PagesService) GetPages(gid interface{}, options ...RequestOptionFunc) (
 
 	return p, resp, nil
 }
-
-// UpdatePages represents the available UpdatePages() options.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/pages.html#update-pages-settings-for-a-project
-type UpdatePagesOptions struct {
-	PagesUniqueDomainEnabled *bool `url:"pages_unique_domain_enabled,omitempty" json:"pages_unique_domain_enabled,omitempty"`
-	PagesHTTPSOnly           *bool `url:"pages_https_only,omitempty" json:"pages_https_only,omitempty"`
-}
-
-// UpdatePages updates Pages settings for a project. The user must have
-// administrator privileges.
-//
-// GitLab API Docs:
-// https://docs.gitlab.com/ee/api/pages.html#update-pages-settings-for-a-project
-func (s *PagesService) UpdatePages(pid interface{}, opt UpdatePagesOptions, options ...RequestOptionFunc) (*Pages, *Response, error) {
-	project, err := parseID(pid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("projects/%s/pages", PathEscape(project))
-
-	req, err := s.client.NewRequest(http.MethodPatch, u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	p := new(Pages)
-	resp, err := s.client.Do(req, p)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return p, resp, nil
-}
diff --git a/project_markdown_uploads.go b/project_markdown_uploads.go
new file mode 100644
index 0000000..a8998e0
--- /dev/null
+++ b/project_markdown_uploads.go
@@ -0,0 +1,210 @@
+//
+// Copyright 2024, Sander van Harmelen
+//
+// 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 gitlab
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"net/http"
+	"time"
+)
+
+// ProjectMarkdownUploadsService handles communication with the project markdown uploads
+// related methods of the GitLab API.
+//
+// Gitlab API docs: https://docs.gitlab.com/ee/api/project_markdown_uploads.html
+type ProjectMarkdownUploadsService struct {
+	client *Client
+}
+
+// ProjectMarkdownUploadedFile represents a single project markdown uploaded file.
+//
+// Gitlab API docs: https://docs.gitlab.com/ee/api/project_markdown_uploads.html
+type ProjectMarkdownUploadedFile struct {
+	ID       int    `json:"id"`
+	Alt      string `json:"alt"`
+	URL      string `json:"url"`
+	FullPath string `json:"full_path"`
+	Markdown string `json:"markdown"`
+}
+
+// ProjectMarkdownUpload represents a single project markdown upload.
+//
+// Gitlab API docs: https://docs.gitlab.com/ee/api/project_markdown_uploads.html
+type ProjectMarkdownUpload struct {
+	ID         int        `json:"id"`
+	Size       int        `json:"size"`
+	Filename   string     `json:"filename"`
+	CreatedAt  *time.Time `json:"created_at"`
+	UploadedBy *User      `json:"uploaded_by"`
+}
+
+// Gets a string representation of a ProjectMarkdownUpload.
+//
+// GitLab API docs: https://docs.gitlab.com/ee/api/project_markdown_uploads.html
+func (m ProjectMarkdownUpload) String() string {
+	return Stringify(m)
+}
+
+// UploadProjectMarkdown uploads a markdown file to a project.
+//
+// GitLab docs:
+// https://docs.gitlab.com/ee/api/project_markdown_uploads.html#upload-a-file
+func (s *ProjectMarkdownUploadsService) UploadProjectMarkdown(pid interface{}, content io.Reader, options ...RequestOptionFunc) (*ProjectMarkdownUploadedFile, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/uploads", PathEscape(project))
+
+	// We need to create the request as a GET request to make sure the options
+	// are set correctly. After the request is created we will overwrite both
+	// the method and the body.
+	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// Overwrite the method and body.
+	req.Method = http.MethodPost
+	req.SetBody(content)
+
+	f := new(ProjectMarkdownUploadedFile)
+	resp, err := s.client.Do(req, f)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return f, resp, nil
+}
+
+// ListProjectMarkdownUploads gets all markdown uploads for a project.
+//
+// GitLab API Docs:
+// https://docs.gitlab.com/ee/api/project_markdown_uploads.html#list-uploads
+func (s *ProjectMarkdownUploadsService) ListProjectMarkdownUploads(pid interface{}, options ...RequestOptionFunc) ([]*ProjectMarkdownUpload, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/uploads", PathEscape(project))
+
+	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var uploads []*ProjectMarkdownUpload
+	resp, err := s.client.Do(req, &uploads)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return uploads, resp, err
+}
+
+// DownloadProjectMarkdownUploadByID downloads a specific upload by ID.
+//
+// GitLab API Docs:
+// https://docs.gitlab.com/ee/api/project_markdown_uploads.html#download-an-uploaded-file-by-id
+func (s *ProjectMarkdownUploadsService) DownloadProjectMarkdownUploadByID(pid interface{}, uploadID int, options ...RequestOptionFunc) ([]byte, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/uploads/%d", PathEscape(project), uploadID)
+
+	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var f bytes.Buffer
+	resp, err := s.client.Do(req, &f)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return f.Bytes(), resp, err
+}
+
+// DownloadProjectMarkdownUploadBySecretAndFilename downloads a specific upload
+// by secret and filename.
+//
+// GitLab API Docs:
+// https://docs.gitlab.com/ee/api/project_markdown_uploads.html#download-an-uploaded-file-by-secret-and-filename
+func (s *ProjectMarkdownUploadsService) DownloadProjectMarkdownUploadBySecretAndFilename(pid interface{}, secret string, filename string, options ...RequestOptionFunc) ([]byte, *Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("projects/%s/uploads/%s/%s", PathEscape(project), PathEscape(secret), PathEscape(filename))
+
+	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var f bytes.Buffer
+	resp, err := s.client.Do(req, &f)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return f.Bytes(), resp, err
+}
+
+// DeleteProjectMarkdownUploadByID deletes an upload by ID.
+//
+// GitLab API Docs:
+// https://docs.gitlab.com/ee/api/project_markdown_uploads.html#delete-an-uploaded-file-by-id
+func (s *ProjectMarkdownUploadsService) DeleteProjectMarkdownUploadByID(pid interface{}, uploadID int, options ...RequestOptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/uploads/%d", PathEscape(project), uploadID)
+
+	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteProjectMarkdownUploadBySecretAndFilename deletes an upload
+// by secret and filename.
+//
+// GitLab API Docs:
+// https://docs.gitlab.com/ee/api/project_markdown_uploads.html#delete-an-uploaded-file-by-secret-and-filename
+func (s *ProjectMarkdownUploadsService) DeleteProjectMarkdownUploadBySecretAndFilename(pid interface{}, secret string, filename string, options ...RequestOptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/uploads/%s/%s",
+		PathEscape(project), PathEscape(secret), PathEscape(filename))
+
+	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
diff --git a/repositories.go b/repositories.go
index 3c59f9a..dde8761 100644
--- a/repositories.go
+++ b/repositories.go
@@ -209,7 +209,6 @@ type Compare struct {
 	Diffs          []*Diff   `json:"diffs"`
 	CompareTimeout bool      `json:"compare_timeout"`
 	CompareSameRef bool      `json:"compare_same_ref"`
-	WebURL         string    `json:"web_url"`
 }
 
 func (c Compare) String() string {
diff --git a/services.go b/services.go
index c8ae0c8..56eeca8 100644
--- a/services.go
+++ b/services.go
@@ -36,31 +36,27 @@ type ServicesService struct {
 //
 // GitLab API docs: https://docs.gitlab.com/ee/api/integrations.html
 type Service struct {
-	ID                             int        `json:"id"`
-	Title                          string     `json:"title"`
-	Slug                           string     `json:"slug"`
-	CreatedAt                      *time.Time `json:"created_at"`
-	UpdatedAt                      *time.Time `json:"updated_at"`
-	Active                         bool       `json:"active"`
-	AlertEvents                    bool       `json:"alert_events"`
-	CommitEvents                   bool       `json:"commit_events"`
-	ConfidentialIssuesEvents       bool       `json:"confidential_issues_events"`
-	ConfidentialNoteEvents         bool       `json:"confidential_note_events"`
-	DeploymentEvents               bool       `json:"deployment_events"`
-	GroupConfidentialMentionEvents bool       `json:"group_confidential_mention_events"`
-	GroupMentionEvents             bool       `json:"group_mention_events"`
-	IncidentEvents                 bool       `json:"incident_events"`
-	IssuesEvents                   bool       `json:"issues_events"`
-	JobEvents                      bool       `json:"job_events"`
-	MergeRequestsEvents            bool       `json:"merge_requests_events"`
-	NoteEvents                     bool       `json:"note_events"`
-	PipelineEvents                 bool       `json:"pipeline_events"`
-	PushEvents                     bool       `json:"push_events"`
-	TagPushEvents                  bool       `json:"tag_push_events"`
-	VulnerabilityEvents            bool       `json:"vulnerability_events"`
-	WikiPageEvents                 bool       `json:"wiki_page_events"`
-	CommentOnEventEnabled          bool       `json:"comment_on_event_enabled"`
-	Inherited                      bool       `json:"inherited"`
+	ID                       int        `json:"id"`
+	Title                    string     `json:"title"`
+	Slug                     string     `json:"slug"`
+	CreatedAt                *time.Time `json:"created_at"`
+	UpdatedAt                *time.Time `json:"updated_at"`
+	Active                   bool       `json:"active"`
+	PushEvents               bool       `json:"push_events"`
+	IssuesEvents             bool       `json:"issues_events"`
+	AlertEvents              bool       `json:"alert_events"`
+	ConfidentialIssuesEvents bool       `json:"confidential_issues_events"`
+	CommitEvents             bool       `json:"commit_events"`
+	MergeRequestsEvents      bool       `json:"merge_requests_events"`
+	CommentOnEventEnabled    bool       `json:"comment_on_event_enabled"`
+	TagPushEvents            bool       `json:"tag_push_events"`
+	NoteEvents               bool       `json:"note_events"`
+	ConfidentialNoteEvents   bool       `json:"confidential_note_events"`
+	PipelineEvents           bool       `json:"pipeline_events"`
+	JobEvents                bool       `json:"job_events"`
+	WikiPageEvents           bool       `json:"wiki_page_events"`
+	VulnerabilityEvents      bool       `json:"vulnerability_events"`
+	DeploymentEvents         bool       `json:"deployment_events"`
 }
 
 // ListServices gets a list of all active services.
@@ -146,25 +142,19 @@ type SetCustomIssueTrackerServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-a-custom-issue-tracker
-func (s *ServicesService) SetCustomIssueTrackerService(pid interface{}, opt *SetCustomIssueTrackerServiceOptions, options ...RequestOptionFunc) (*CustomIssueTrackerService, *Response, error) {
+func (s *ServicesService) SetCustomIssueTrackerService(pid interface{}, opt *SetCustomIssueTrackerServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/custom-issue-tracker", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(CustomIssueTrackerService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteCustomIssueTrackerService deletes Custom Issue Tracker service settings for a project.
@@ -252,25 +242,19 @@ type SetDataDogServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-datadog
-func (s *ServicesService) SetDataDogService(pid interface{}, opt *SetDataDogServiceOptions, options ...RequestOptionFunc) (*DataDogService, *Response, error) {
+func (s *ServicesService) SetDataDogService(pid interface{}, opt *SetDataDogServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		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, nil, err
-	}
-
-	svc := new(DataDogService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteDataDogService deletes the DataDog service settings for a project.
@@ -374,25 +358,19 @@ type SetDiscordServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-discord-notifications
-func (s *ServicesService) SetDiscordService(pid interface{}, opt *SetDiscordServiceOptions, options ...RequestOptionFunc) (*DiscordService, *Response, error) {
+func (s *ServicesService) SetDiscordService(pid interface{}, opt *SetDiscordServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/discord", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(DiscordService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, resp, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteDiscordService deletes Discord service settings for a project.
@@ -475,25 +453,19 @@ type SetDroneCIServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-drone
-func (s *ServicesService) SetDroneCIService(pid interface{}, opt *SetDroneCIServiceOptions, options ...RequestOptionFunc) (*DroneCIService, *Response, error) {
+func (s *ServicesService) SetDroneCIService(pid interface{}, opt *SetDroneCIServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/drone-ci", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(DroneCIService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteDroneCIService deletes Drone CI service settings for a project.
@@ -580,25 +552,19 @@ type SetEmailsOnPushServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-emails-on-push
-func (s *ServicesService) SetEmailsOnPushService(pid interface{}, opt *SetEmailsOnPushServiceOptions, options ...RequestOptionFunc) (*EmailsOnPushService, *Response, error) {
+func (s *ServicesService) SetEmailsOnPushService(pid interface{}, opt *SetEmailsOnPushServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/integrations/emails-on-push", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(EmailsOnPushService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteEmailsOnPushService deletes Emails on Push service settings for a project.
@@ -675,25 +641,19 @@ type SetExternalWikiServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-an-external-wiki
-func (s *ServicesService) SetExternalWikiService(pid interface{}, opt *SetExternalWikiServiceOptions, options ...RequestOptionFunc) (*ExternalWikiService, *Response, error) {
+func (s *ServicesService) SetExternalWikiService(pid interface{}, opt *SetExternalWikiServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/external-wiki", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(ExternalWikiService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteExternalWikiService deletes External Wiki service for project.
@@ -773,25 +733,19 @@ type SetGithubServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-github
-func (s *ServicesService) SetGithubService(pid interface{}, opt *SetGithubServiceOptions, options ...RequestOptionFunc) (*GithubService, *Response, error) {
+func (s *ServicesService) SetGithubService(pid interface{}, opt *SetGithubServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/github", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(GithubService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteGithubService deletes Github service for a project
@@ -876,25 +830,19 @@ type SetHarborServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-harbor
-func (s *ServicesService) SetHarborService(pid interface{}, opt *SetHarborServiceOptions, options ...RequestOptionFunc) (*HarborService, *Response, error) {
+func (s *ServicesService) SetHarborService(pid interface{}, opt *SetHarborServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/integrations/harbor", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(HarborService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteHarborService deletes Harbor service for a project.
@@ -930,24 +878,22 @@ type SlackApplication struct {
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#gitlab-for-slack-app
 type SlackApplicationProperties struct {
-	Channel                    string `json:"channel"`
-	NotifyOnlyBrokenPipelines  bool   `json:"notify_only_broken_pipelines"`
-	BranchesToBeNotified       string `json:"branches_to_be_notified"`
-	LabelsToBeNotified         string `json:"labels_to_be_notified"`
-	LabelsToBeNotifiedBehavior string `json:"labels_to_be_notified_behavior"`
-	PushChannel                string `json:"push_channel"`
-	IssueChannel               string `json:"issue_channel"`
-	ConfidentialIssueChannel   string `json:"confidential_issue_channel"`
-	MergeRequestChannel        string `json:"merge_request_channel"`
-	NoteChannel                string `json:"note_channel"`
-	ConfidentialNoteChannel    string `json:"confidential_note_channel"`
-	TagPushChannel             string `json:"tag_push_channel"`
-	PipelineChannel            string `json:"pipeline_channel"`
-	WikiPageChannel            string `json:"wiki_page_channel"`
-	DeploymentChannel          string `json:"deployment_channel"`
-	IncidentChannel            string `json:"incident_channel"`
-	VulnerabilityChannel       string `json:"vulnerability_channel"`
-	AlertChannel               string `json:"alert_channel"`
+	Channel                   string `json:"channel"`
+	NotifyOnlyBrokenPipelines bool   `json:"notify_only_broken_pipelines"`
+	BranchesToBeNotified      string `json:"branches_to_be_notified"`
+	AlertEvents               bool   `json:"alert_events"`
+	IssuesEvents              bool   `json:"issues_events"`
+	ConfidentialIssuesEvents  bool   `json:"confidential_issues_events"`
+	MergeRequestsEvents       bool   `json:"merge_requests_events"`
+	NoteEvents                bool   `json:"note_events"`
+	ConfidentialNoteEvents    bool   `json:"confidential_note_events"`
+	DeploymentEvents          bool   `json:"deployment_events"`
+	IncidentsEvents           bool   `json:"incidents_events"`
+	PipelineEvents            bool   `json:"pipeline_events"`
+	PushEvents                bool   `json:"push_events"`
+	TagPushEvents             bool   `json:"tag_push_events"`
+	VulnerabilityEvents       bool   `json:"vulnerability_events"`
+	WikiPageEvents            bool   `json:"wiki_page_events"`
 
 	// Deprecated: This parameter has been replaced with BranchesToBeNotified.
 	NotifyOnlyDefaultBranch bool `json:"notify_only_default_branch"`
@@ -985,38 +931,22 @@ func (s *ServicesService) GetSlackApplication(pid interface{}, options ...Reques
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-gitlab-for-slack-app
 type SetSlackApplicationOptions struct {
-	Channel                    *string `url:"channel,omitempty" json:"channel,omitempty"`
-	NotifyOnlyBrokenPipelines  *bool   `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"`
-	BranchesToBeNotified       *string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"`
-	AlertEvents                *bool   `url:"alert_events,omitempty" json:"alert_events,omitempty"`
-	IssuesEvents               *bool   `url:"issues_events,omitempty" json:"issues_events,omitempty"`
-	ConfidentialIssuesEvents   *bool   `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
-	MergeRequestsEvents        *bool   `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
-	NoteEvents                 *bool   `url:"note_events,omitempty" json:"note_events,omitempty"`
-	ConfidentialNoteEvents     *bool   `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"`
-	DeploymentEvents           *bool   `url:"deployment_events,omitempty" json:"deployment_events,omitempty"`
-	IncidentsEvents            *bool   `url:"incidents_events,omitempty" json:"incidents_events,omitempty"`
-	PipelineEvents             *bool   `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
-	PushEvents                 *bool   `url:"push_events,omitempty" json:"push_events,omitempty"`
-	TagPushEvents              *bool   `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
-	VulnerabilityEvents        *bool   `url:"vulnerability_events,omitempty" json:"vulnerability_events,omitempty"`
-	WikiPageEvents             *bool   `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
-	LabelsToBeNotified         *string `url:"labels_to_be_notified,omitempty" json:"labels_to_be_notified,omitempty"`
-	LabelsToBeNotifiedBehavior *string `url:"labels_to_be_notified_behavior,omitempty" json:"labels_to_be_notified_behavior,omitempty"`
-	PushChannel                *string `url:"push_channel,omitempty" json:"push_channel,omitempty"`
-	IssueChannel               *string `url:"issue_channel,omitempty" json:"issue_channel,omitempty"`
-	ConfidentialIssueChannel   *string `url:"confidential_issue_channel,omitempty" json:"confidential_issue_channel,omitempty"`
-	MergeRequestChannel        *string `url:"merge_request_channel,omitempty" json:"merge_request_channel,omitempty"`
-	NoteChannel                *string `url:"note_channel,omitempty" json:"note_channel,omitempty"`
-	ConfidentialNoteChannel    *string `url:"confidential_note_channel,omitempty" json:"confidential_note_channel,omitempty"`
-	TagPushChannel             *string `url:"tag_push_channel,omitempty" json:"tag_push_channel,omitempty"`
-	PipelineChannel            *string `url:"pipeline_channel,omitempty" json:"pipeline_channel,omitempty"`
-	WikiPageChannel            *string `url:"wiki_page_channel,omitempty" json:"wiki_page_channel,omitempty"`
-	DeploymentChannel          *string `url:"deployment_channel,omitempty" json:"deployment_channel,omitempty"`
-	IncidentChannel            *string `url:"incident_channel,omitempty" json:"incident_channel,omitempty"`
-	VulnerabilityChannel       *string `url:"vulnerability_channel,omitempty" json:"vulnerability_channel,omitempty"`
-	AlertChannel               *string `url:"alert_channel,omitempty" json:"alert_channel,omitempty"`
-	UseInheritedSettings       *bool   `url:"use_inherited_settings,omitempty" json:"use_inherited_settings,omitempty"`
+	Channel                   *string `url:"channel,omitempty" json:"channel,omitempty"`
+	NotifyOnlyBrokenPipelines *bool   `url:"notify_only_broken_pipelines,omitempty" json:"notify_only_broken_pipelines,omitempty"`
+	BranchesToBeNotified      *string `url:"branches_to_be_notified,omitempty" json:"branches_to_be_notified,omitempty"`
+	AlertEvents               *bool   `url:"alert_events,omitempty" json:"alert_events,omitempty"`
+	IssuesEvents              *bool   `url:"issues_events,omitempty" json:"issues_events,omitempty"`
+	ConfidentialIssuesEvents  *bool   `url:"confidential_issues_events,omitempty" json:"confidential_issues_events,omitempty"`
+	MergeRequestsEvents       *bool   `url:"merge_requests_events,omitempty" json:"merge_requests_events,omitempty"`
+	NoteEvents                *bool   `url:"note_events,omitempty" json:"note_events,omitempty"`
+	ConfidentialNoteEvents    *bool   `url:"confidential_note_events,omitempty" json:"confidential_note_events,omitempty"`
+	DeploymentEvents          *bool   `url:"deployment_events,omitempty" json:"deployment_events,omitempty"`
+	IncidentsEvents           *bool   `url:"incidents_events,omitempty" json:"incidents_events,omitempty"`
+	PipelineEvents            *bool   `url:"pipeline_events,omitempty" json:"pipeline_events,omitempty"`
+	PushEvents                *bool   `url:"push_events,omitempty" json:"push_events,omitempty"`
+	TagPushEvents             *bool   `url:"tag_push_events,omitempty" json:"tag_push_events,omitempty"`
+	VulnerabilityEvents       *bool   `url:"vulnerability_events,omitempty" json:"vulnerability_events,omitempty"`
+	WikiPageEvents            *bool   `url:"wiki_page_events,omitempty" json:"wiki_page_events,omitempty"`
 
 	// Deprecated: This parameter has been replaced with BranchesToBeNotified.
 	NotifyOnlyDefaultBranch *bool `url:"notify_only_default_branch,omitempty" json:"notify_only_default_branch,omitempty"`
@@ -1026,25 +956,19 @@ type SetSlackApplicationOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-gitlab-for-slack-app
-func (s *ServicesService) SetSlackApplication(pid interface{}, opt *SetSlackApplicationOptions, options ...RequestOptionFunc) (*SlackApplication, *Response, error) {
+func (s *ServicesService) SetSlackApplication(pid interface{}, opt *SetSlackApplicationOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/integrations/gitlab-slack-application", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(SlackApplication)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DisableSlackApplication disable the GitLab for Slack app integration for a project.
@@ -1227,25 +1151,19 @@ type SetJenkinsCIServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-jenkins
-func (s *ServicesService) SetJenkinsCIService(pid interface{}, opt *SetJenkinsCIServiceOptions, options ...RequestOptionFunc) (*JenkinsCIService, *Response, error) {
+func (s *ServicesService) SetJenkinsCIService(pid interface{}, opt *SetJenkinsCIServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/jenkins", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(JenkinsCIService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteJenkinsCIService deletes Jenkins CI service for project.
@@ -1388,25 +1306,19 @@ type SetJiraServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#edit-jira-service
-func (s *ServicesService) SetJiraService(pid interface{}, opt *SetJiraServiceOptions, options ...RequestOptionFunc) (*JiraService, *Response, error) {
+func (s *ServicesService) SetJiraService(pid interface{}, opt *SetJiraServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/integrations/jira", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(JiraService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteJiraService deletes Jira service for project.
@@ -1515,50 +1427,6 @@ type SetMattermostServiceOptions struct {
 	WikiPageChannel           *string `url:"wiki_page_channel,omitempty" json:"wiki_page_channel,omitempty"`
 }
 
-// SetMattermostService sets Mattermost service for a project.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/integrations.html#createedit-mattermost-notifications-service
-func (s *ServicesService) SetMattermostService(pid interface{}, opt *SetMattermostServiceOptions, options ...RequestOptionFunc) (*MattermostService, *Response, error) {
-	project, err := parseID(pid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("projects/%s/services/mattermost", PathEscape(project))
-
-	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(MattermostService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	return svc, resp, nil
-}
-
-// DeleteMattermostService deletes Mattermost service for project.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/integrations.html#delete-mattermost-notifications-service
-func (s *ServicesService) DeleteMattermostService(pid interface{}, options ...RequestOptionFunc) (*Response, error) {
-	project, err := parseID(pid)
-	if err != nil {
-		return nil, err
-	}
-	u := fmt.Sprintf("projects/%s/services/mattermost", PathEscape(project))
-
-	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
-	if err != nil {
-		return nil, err
-	}
-
-	return s.client.Do(req, nil)
-}
-
 // MattermostSlashCommandsService represents Mattermost slash commands settings.
 //
 // GitLab API docs:
@@ -1616,25 +1484,19 @@ type SetMattermostSlashCommandsServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#createedit-mattermost-slash-command-integration
-func (s *ServicesService) SetMattermostSlashCommandsService(pid interface{}, opt *SetMattermostSlashCommandsServiceOptions, options ...RequestOptionFunc) (*MattermostSlashCommandsService, *Response, error) {
+func (s *ServicesService) SetMattermostSlashCommandsService(pid interface{}, opt *SetMattermostSlashCommandsServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/mattermost-slash-commands", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(MattermostSlashCommandsService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteMattermostSlashCommandsService deletes Mattermost slash commands service for project.
@@ -1656,6 +1518,44 @@ func (s *ServicesService) DeleteMattermostSlashCommandsService(pid interface{},
 	return s.client.Do(req, nil)
 }
 
+// SetMattermostService sets Mattermost service for a project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/integrations.html#createedit-mattermost-notifications-service
+func (s *ServicesService) SetMattermostService(pid interface{}, opt *SetMattermostServiceOptions, options ...RequestOptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/mattermost", PathEscape(project))
+
+	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteMattermostService deletes Mattermost service for project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/integrations.html#delete-mattermost-notifications-service
+func (s *ServicesService) DeleteMattermostService(pid interface{}, options ...RequestOptionFunc) (*Response, error) {
+	project, err := parseID(pid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("projects/%s/services/mattermost", PathEscape(project))
+
+	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
 // MicrosoftTeamsService represents Microsoft Teams service settings.
 //
 // GitLab API docs:
@@ -1732,25 +1632,18 @@ type SetMicrosoftTeamsServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#create-edit-microsoft-teams-service
-func (s *ServicesService) SetMicrosoftTeamsService(pid interface{}, opt *SetMicrosoftTeamsServiceOptions, options ...RequestOptionFunc) (*MicrosoftTeamsService, *Response, error) {
+func (s *ServicesService) SetMicrosoftTeamsService(pid interface{}, opt *SetMicrosoftTeamsServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/microsoft-teams", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(MicrosoftTeamsService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
-
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteMicrosoftTeamsService deletes Microsoft Teams service for project.
@@ -1835,25 +1728,19 @@ type SetPipelinesEmailServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#pipeline-emails
-func (s *ServicesService) SetPipelinesEmailService(pid interface{}, opt *SetPipelinesEmailServiceOptions, options ...RequestOptionFunc) (*PipelinesEmailService, *Response, error) {
+func (s *ServicesService) SetPipelinesEmailService(pid interface{}, opt *SetPipelinesEmailServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/pipelines-email", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(PipelinesEmailService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeletePipelinesEmailService deletes Pipelines Email service settings for a project.
@@ -1934,25 +1821,19 @@ type SetPrometheusServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#createedit-prometheus-service
-func (s *ServicesService) SetPrometheusService(pid interface{}, opt *SetPrometheusServiceOptions, options ...RequestOptionFunc) (*PrometheusService, *Response, error) {
+func (s *ServicesService) SetPrometheusService(pid interface{}, opt *SetPrometheusServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/prometheus", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(PrometheusService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeletePrometheusService deletes Prometheus service settings for a project.
@@ -2035,25 +1916,19 @@ type SetRedmineServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-redmine
-func (s *ServicesService) SetRedmineService(pid interface{}, opt *SetRedmineServiceOptions, options ...RequestOptionFunc) (*RedmineService, *Response, error) {
+func (s *ServicesService) SetRedmineService(pid interface{}, opt *SetRedmineServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/integrations/redmine", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(RedmineService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteRedmineService deletes Redmine service for project.
@@ -2174,25 +2049,19 @@ type SetSlackServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#edit-slack-service
-func (s *ServicesService) SetSlackService(pid interface{}, opt *SetSlackServiceOptions, options ...RequestOptionFunc) (*SlackService, *Response, error) {
+func (s *ServicesService) SetSlackService(pid interface{}, opt *SetSlackServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/slack", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(SlackService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteSlackService deletes Slack service for project.
@@ -2269,25 +2138,19 @@ type SetSlackSlashCommandsServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/13.12/ee/api/integrations.html#createedit-slack-slash-command-service
-func (s *ServicesService) SetSlackSlashCommandsService(pid interface{}, opt *SetSlackSlashCommandsServiceOptions, options ...RequestOptionFunc) (*SlackSlashCommandsService, *Response, error) {
+func (s *ServicesService) SetSlackSlashCommandsService(pid interface{}, opt *SetSlackSlashCommandsServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/slack-slash-commands", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(SlackSlashCommandsService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteSlackSlashCommandsService deletes Slack slash commands service for project.
@@ -2378,25 +2241,19 @@ type SetTelegramServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#set-up-telegram
-func (s *ServicesService) SetTelegramService(pid interface{}, opt *SetTelegramServiceOptions, options ...RequestOptionFunc) (*TelegramService, *Response, error) {
+func (s *ServicesService) SetTelegramService(pid interface{}, opt *SetTelegramServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/telegram", PathEscape(project))
 
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	svc := new(TelegramService)
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteTelegramService deletes Telegram service for project.
@@ -2479,25 +2336,19 @@ type SetYouTrackServiceOptions struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/integrations.html#createedit-youtrack-service
-func (s *ServicesService) SetYouTrackService(pid interface{}, opt *SetYouTrackServiceOptions, options ...RequestOptionFunc) (*YouTrackService, *Response, error) {
+func (s *ServicesService) SetYouTrackService(pid interface{}, opt *SetYouTrackServiceOptions, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/services/youtrack", PathEscape(project))
 
-	svc := new(YouTrackService)
 	req, err := s.client.NewRequest(http.MethodPut, u, opt, options)
 	if err != nil {
-		return nil, nil, err
-	}
-
-	resp, err := s.client.Do(req, svc)
-	if err != nil {
-		return nil, nil, err
+		return nil, err
 	}
 
-	return svc, resp, nil
+	return s.client.Do(req, nil)
 }
 
 // DeleteYouTrackService deletes YouTrack service settings for a project.


Test patch:
diff --git a/deploy_keys_test.go b/deploy_keys_test.go
index d760bc6..260ee1c 100644
--- a/deploy_keys_test.go
+++ b/deploy_keys_test.go
@@ -225,8 +225,7 @@ func TestAddDeployKey(t *testing.T) {
 			"id" : 12,
 			"title" : "My deploy key",
 			"can_push": true,
-			"created_at" : "2015-08-29T12:44:31.550Z",
-			"expires_at": null
+			"created_at" : "2015-08-29T12:44:31.550Z"
 		 }`)
 	})
 
@@ -257,55 +256,6 @@ func TestAddDeployKey(t *testing.T) {
 	}
 }
 
-func TestAddDeployKey_withExpiresAt(t *testing.T) {
-	mux, client := setup(t)
-
-	mux.HandleFunc("/api/v4/projects/5/deploy_keys", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, http.MethodPost)
-		fmt.Fprintf(w, `{
-			"key" : "ssh-rsa AAAA...",
-			"id" : 12,
-			"title" : "My deploy key",
-			"can_push": true,
-			"created_at" : "2015-08-29T12:44:31.550Z",
-			"expires_at": "2999-03-01T00:00:00.000Z"
-		 }`)
-	})
-
-	expiresAt, err := time.Parse(timeLayout, "2999-03-01T00:00:00.000Z")
-	if err != nil {
-		t.Errorf("DeployKeys.AddDeployKey returned an error while parsing time: %v", err)
-	}
-
-	opt := &AddDeployKeyOptions{
-		Key:       Ptr("ssh-rsa AAAA..."),
-		Title:     Ptr("My deploy key"),
-		CanPush:   Ptr(true),
-		ExpiresAt: &expiresAt,
-	}
-	deployKey, _, err := client.DeployKeys.AddDeployKey(5, opt)
-	if err != nil {
-		t.Errorf("DeployKey.AddDeployKey returned error: %v", err)
-	}
-
-	createdAt, err := time.Parse(timeLayout, "2015-08-29T12:44:31.550Z")
-	if err != nil {
-		t.Errorf("DeployKeys.AddDeployKey returned an error while parsing time: %v", err)
-	}
-
-	want := &ProjectDeployKey{
-		Title:     "My deploy key",
-		ID:        12,
-		Key:       "ssh-rsa AAAA...",
-		CreatedAt: &createdAt,
-		CanPush:   true,
-		ExpiresAt: &expiresAt,
-	}
-	if !reflect.DeepEqual(want, deployKey) {
-		t.Errorf("DeployKeys.AddDeployKey returned %+v, want %+v", deployKey, want)
-	}
-}
-
 func TestDeleteDeployKey(t *testing.T) {
 	mux, client := setup(t)
 
diff --git a/pages_test.go b/pages_test.go
index 4101caf..0168253 100644
--- a/pages_test.go
+++ b/pages_test.go
@@ -78,47 +78,3 @@ func TestGetPages(t *testing.T) {
 	require.NotNil(t, resp)
 	require.Equal(t, want, p)
 }
-
-func TestUpdatePages(t *testing.T) {
-	mux, client := setup(t)
-	mux.HandleFunc("/api/v4/projects/2/pages", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, http.MethodPatch)
-		fmt.Fprint(w, `
-		  {
-			"url": "https://ssl.domain.example",
-			"deployments": [
-			  {
-				"created_at": "2021-04-27T21:27:38.584Z",
-				"url": "https://ssl.domain.example/",
-				"path_prefix": "",
-				"root_directory": null
-			  }
-			],
-			"is_unique_domain_enabled": true,
-			"force_https": false
-		  }
-		`)
-	})
-
-	want := &Pages{
-		URL:                   "https://ssl.domain.example",
-		IsUniqueDomainEnabled: true,
-		ForceHTTPS:            false,
-		Deployments: []*PagesDeployment{
-			{
-				CreatedAt:     time.Date(2021, time.April, 27, 21, 27, 38, 584000000, time.UTC),
-				URL:           "https://ssl.domain.example/",
-				PathPrefix:    "",
-				RootDirectory: "",
-			},
-		},
-	}
-
-	p, resp, err := client.Pages.UpdatePages(2, UpdatePagesOptions{
-		PagesUniqueDomainEnabled: Ptr(true),
-		PagesHTTPSOnly:           Ptr(false),
-	})
-	require.NoError(t, err)
-	require.NotNil(t, resp)
-	require.Equal(t, want, p)
-}
diff --git a/project_markdown_uploads_test.go b/project_markdown_uploads_test.go
new file mode 100644
index 0000000..b83305a
--- /dev/null
+++ b/project_markdown_uploads_test.go
@@ -0,0 +1,163 @@
+package gitlab
+
+import (
+	"fmt"
+	"net/http"
+	"strings"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestProjectMarkdownUploads_UploadProjectMarkdown(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/uploads", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		fmt.Fprint(w, `
+			{
+				"id": 5,
+				"alt": "dk",
+				"url": "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
+				"full_path": "/-/project/1234/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
+				"markdown": "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)"
+			}
+		`)
+	})
+
+	want := &ProjectMarkdownUploadedFile{
+		ID:       5,
+		Alt:      "dk",
+		URL:      "/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
+		FullPath: "/-/project/1234/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png",
+		Markdown: "![dk](/uploads/66dbcd21ec5d24ed6ea225176098d52b/dk.png)",
+	}
+
+	content := strings.NewReader("bar = baz")
+	upload, resp, err := client.ProjectMarkdownUploads.UploadProjectMarkdown(1, content)
+	require.NoError(t, err)
+	require.NotNil(t, resp)
+	require.Equal(t, want, upload)
+}
+
+func TestProjectMarkdownUploads_ListProjectMarkdownUploads(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/uploads", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, `
+			[
+				{
+					"id": 1,
+					"size": 1024,
+					"filename": "image.png",
+					"created_at":"2024-06-20T15:53:03.000Z",
+					"uploaded_by": {
+					"id": 18,
+					"name" : "Alexandra Bashirian",
+					"username" : "eileen.lowe"
+					}
+				},
+				{
+					"id": 2,
+					"size": 512,
+					"filename": "other-image.png",
+					"created_at":"2024-06-19T15:53:03.000Z",
+					"uploaded_by": null
+				}
+			]
+		`)
+	})
+
+	created1 := time.Date(2024, 6, 20, 15, 53, 3, 0, time.UTC)
+	created2 := time.Date(2024, 6, 19, 15, 53, 3, 0, time.UTC)
+	want := []*ProjectMarkdownUpload{
+		{
+			ID:        1,
+			Size:      1024,
+			Filename:  "image.png",
+			CreatedAt: &created1,
+			UploadedBy: &User{
+				ID:       18,
+				Name:     "Alexandra Bashirian",
+				Username: "eileen.lowe",
+			},
+		},
+		{
+			ID:        2,
+			Size:      512,
+			Filename:  "other-image.png",
+			CreatedAt: &created2,
+		},
+	}
+
+	uploads, resp, err := client.ProjectMarkdownUploads.ListProjectMarkdownUploads(1)
+	require.NoError(t, err)
+	require.NotNil(t, resp)
+	require.Equal(t, want, uploads)
+}
+
+func TestProjectMarkdownUploads_DownloadProjectMarkdownUploadByID(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/uploads/2", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, strings.TrimSpace(`
+			bar = baz
+		`))
+	})
+
+	want := []byte("bar = baz")
+
+	bytes, resp, err := client.ProjectMarkdownUploads.DownloadProjectMarkdownUploadByID(1, 2)
+	require.NoError(t, err)
+	require.NotNil(t, resp)
+	require.Equal(t, want, bytes)
+}
+
+func TestProjectMarkdownUploads_DownloadProjectMarkdownUploadBySecretAndFilename(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/uploads/secret/filename", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodGet)
+		fmt.Fprint(w, strings.TrimSpace(`
+			bar = baz
+		`))
+	})
+
+	want := []byte("bar = baz")
+
+	bytes, resp, err := client.ProjectMarkdownUploads.DownloadProjectMarkdownUploadBySecretAndFilename(1, "secret", "filename")
+	require.NoError(t, err)
+	require.NotNil(t, resp)
+	require.Equal(t, want, bytes)
+}
+
+func TestProjectMarkdownUploads_DeleteProjectMarkdownUploadByID(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/uploads/2", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodDelete)
+		w.WriteHeader(204)
+	})
+
+	resp, err := client.ProjectMarkdownUploads.DeleteProjectMarkdownUploadByID(1, 2)
+	require.NoError(t, err)
+	require.NotNil(t, resp)
+	require.Equal(t, 204, resp.StatusCode)
+}
+
+func TestProjectMarkdownUploads_DeleteProjectMarkdownUploadBySecretAndFilename(t *testing.T) {
+	mux, client := setup(t)
+
+	mux.HandleFunc("/api/v4/projects/1/uploads/secret/filename", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodDelete)
+		w.WriteHeader(204)
+	})
+
+	resp, err := client.ProjectMarkdownUploads.DeleteProjectMarkdownUploadBySecretAndFilename(1, "secret", "filename")
+	require.NoError(t, err)
+	require.NotNil(t, resp)
+	require.Equal(t, 204, resp.StatusCode)
+}
diff --git a/repositories_test.go b/repositories_test.go
index 9130def..4610bf3 100644
--- a/repositories_test.go
+++ b/repositories_test.go
@@ -281,7 +281,6 @@ func TestRepositoriesService_Compare(t *testing.T) {
 		}},
 		CompareTimeout: false,
 		CompareSameRef: false,
-		WebURL:         "https://gitlab.example.com/thedude/gitlab-foss/-/compare/ae73cb07c9eeaf35924a10f713b364d32b2dd34f...0b4bc9a49b562e85de7cc9e834518ea6828729b9",
 	}
 
 	c, resp, err := client.Repositories.Compare("12d65c8dd2b2676fa3ac47d955accc085a37a9c1", opt, nil)
diff --git a/services_test.go b/services_test.go
index 543c28d..53596ea 100644
--- a/services_test.go
+++ b/services_test.go
@@ -85,7 +85,6 @@ func TestSetCustomIssueTrackerService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/custom-issue-tracker", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetCustomIssueTrackerServiceOptions{
@@ -94,7 +93,7 @@ func TestSetCustomIssueTrackerService(t *testing.T) {
 		ProjectURL:  Ptr("3"),
 	}
 
-	_, _, err := client.Services.SetCustomIssueTrackerService(1, opt)
+	_, err := client.Services.SetCustomIssueTrackerService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetCustomIssueTrackerService returns an error: %v", err)
 	}
@@ -157,7 +156,6 @@ func TestSetDataDogService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/datadog", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetDataDogServiceOptions{
@@ -170,7 +168,7 @@ func TestSetDataDogService(t *testing.T) {
 		ArchiveTraceEvents: Bool(false),
 	}
 
-	_, _, err := client.Services.SetDataDogService(1, opt)
+	_, err := client.Services.SetDataDogService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetDataDogService returns an error: %v", err)
 	}
@@ -212,14 +210,13 @@ func TestSetDiscordService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/discord", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetDiscordServiceOptions{
 		WebHook: Ptr("webhook_uri"),
 	}
 
-	_, _, err := client.Services.SetDiscordService(1, opt)
+	_, err := client.Services.SetDiscordService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetDiscordService returns an error: %v", err)
 	}
@@ -261,12 +258,11 @@ func TestSetDroneCIService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/drone-ci", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetDroneCIServiceOptions{Ptr("token"), Ptr("drone-url"), Ptr(true), nil, nil, nil}
 
-	_, _, err := client.Services.SetDroneCIService(1, opt)
+	_, err := client.Services.SetDroneCIService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetDroneCIService returns an error: %v", err)
 	}
@@ -308,12 +304,11 @@ func TestSetEmailsOnPushService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/integrations/emails-on-push", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetEmailsOnPushServiceOptions{Ptr("t"), Ptr(true), Ptr(true), Ptr(true), Ptr(true), Ptr("t")}
 
-	_, _, err := client.Services.SetEmailsOnPushService(1, opt)
+	_, err := client.Services.SetEmailsOnPushService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetEmailsOnPushService returns an error: %v", err)
 	}
@@ -355,7 +350,6 @@ func TestSetHarborService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/integrations/harbor", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetHarborServiceOptions{
@@ -366,7 +360,7 @@ func TestSetHarborService(t *testing.T) {
 		UseInheritedSettings: Ptr(false),
 	}
 
-	_, _, err := client.Services.SetHarborService(1, opt)
+	_, err := client.Services.SetHarborService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetHarborService returns an error: %v", err)
 	}
@@ -408,12 +402,11 @@ func TestSetSlackApplication(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/integrations/gitlab-slack-application", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetSlackApplicationOptions{Channel: Ptr("#channel1"), NoteEvents: Ptr(true), AlertEvents: Ptr(true)}
 
-	_, _, err := client.Services.SetSlackApplication(1, opt)
+	_, err := client.Services.SetSlackApplication(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetSlackApplication returns an error: %v", err)
 	}
@@ -508,7 +501,6 @@ func TestSetJiraService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/integrations/jira", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetJiraServiceOptions{
@@ -529,7 +521,7 @@ func TestSetJiraService(t *testing.T) {
 		UseInheritedSettings:         Ptr(true),
 	}
 
-	_, _, err := client.Services.SetJiraService(1, opt)
+	_, err := client.Services.SetJiraService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetJiraService returns an error: %v", err)
 	}
@@ -540,7 +532,6 @@ func TestSetJiraServiceProjecKeys(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/integrations/jira", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetJiraServiceOptions{
@@ -561,7 +552,7 @@ func TestSetJiraServiceProjecKeys(t *testing.T) {
 		UseInheritedSettings:         Ptr(true),
 	}
 
-	_, _, err := client.Services.SetJiraService(1, opt)
+	_, err := client.Services.SetJiraService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetJiraService returns an error: %v", err)
 	}
@@ -572,7 +563,6 @@ func TestSetJiraServiceAuthTypeBasicAuth(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/integrations/jira", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetJiraServiceOptions{
@@ -582,7 +572,7 @@ func TestSetJiraServiceAuthTypeBasicAuth(t *testing.T) {
 		JiraAuthType: Ptr(0),
 	}
 
-	_, _, err := client.Services.SetJiraService(1, opt)
+	_, err := client.Services.SetJiraService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetJiraService returns an error: %v", err)
 	}
@@ -593,7 +583,6 @@ func TestSetJiraServiceAuthTypeTokenAuth(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/integrations/jira", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetJiraServiceOptions{
@@ -602,7 +591,7 @@ func TestSetJiraServiceAuthTypeTokenAuth(t *testing.T) {
 		JiraAuthType: Ptr(1),
 	}
 
-	_, _, err := client.Services.SetJiraService(1, opt)
+	_, err := client.Services.SetJiraService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetJiraService returns an error: %v", err)
 	}
@@ -644,7 +633,6 @@ func TestSetMattermostService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/mattermost", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetMattermostServiceOptions{
@@ -653,7 +641,7 @@ func TestSetMattermostService(t *testing.T) {
 		Channel:  Ptr("#development"),
 	}
 
-	_, _, err := client.Services.SetMattermostService(1, opt)
+	_, err := client.Services.SetMattermostService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetMasttermostService returns an error: %v", err)
 	}
@@ -695,7 +683,6 @@ func TestSetMattermostSlashCommandsService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/mattermost-slash-commands", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetMattermostSlashCommandsServiceOptions{
@@ -703,7 +690,7 @@ func TestSetMattermostSlashCommandsService(t *testing.T) {
 		Username: Ptr("username"),
 	}
 
-	_, _, err := client.Services.SetMattermostSlashCommandsService(1, opt)
+	_, err := client.Services.SetMattermostSlashCommandsService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetMattermostSlashCommandsService returns an error: %v", err)
 	}
@@ -745,7 +732,6 @@ func TestSetPipelinesEmailService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/pipelines-email", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetPipelinesEmailServiceOptions{
@@ -757,7 +743,7 @@ func TestSetPipelinesEmailService(t *testing.T) {
 		PipelineEvents:            nil,
 	}
 
-	_, _, err := client.Services.SetPipelinesEmailService(1, opt)
+	_, err := client.Services.SetPipelinesEmailService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetPipelinesEmailService returns an error: %v", err)
 	}
@@ -799,12 +785,11 @@ func TestSetPrometheusService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/prometheus", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetPrometheusServiceOptions{Ptr("t"), Ptr("u"), Ptr("a")}
 
-	_, _, err := client.Services.SetPrometheusService(1, opt)
+	_, err := client.Services.SetPrometheusService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetDroneCIService returns an error: %v", err)
 	}
@@ -846,12 +831,11 @@ func TestSetRedmineService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/integrations/redmine", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetRedmineServiceOptions{Ptr("t"), Ptr("u"), Ptr("a"), Ptr(false)}
 
-	_, _, err := client.Services.SetRedmineService(1, opt)
+	_, err := client.Services.SetRedmineService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetRedmineService returns an error: %v", err)
 	}
@@ -893,7 +877,6 @@ func TestSetSlackService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/slack", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetSlackServiceOptions{
@@ -902,7 +885,7 @@ func TestSetSlackService(t *testing.T) {
 		Channel:  Ptr("#development"),
 	}
 
-	_, _, err := client.Services.SetSlackService(1, opt)
+	_, err := client.Services.SetSlackService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetSlackService returns an error: %v", err)
 	}
@@ -944,14 +927,13 @@ func TestSetSlackSlashCommandsService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/slack-slash-commands", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetSlackSlashCommandsServiceOptions{
 		Token: Ptr("token"),
 	}
 
-	_, _, err := client.Services.SetSlackSlashCommandsService(1, opt)
+	_, err := client.Services.SetSlackSlashCommandsService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetSlackSlashCommandsService returns an error: %v", err)
 	}
@@ -1054,7 +1036,6 @@ func TestSetTelegramService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/telegram", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetTelegramServiceOptions{
@@ -1073,7 +1054,7 @@ func TestSetTelegramService(t *testing.T) {
 		WikiPageEvents:            Ptr(true),
 	}
 
-	_, _, err := client.Services.SetTelegramService(1, opt)
+	_, err := client.Services.SetTelegramService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetTelegramService returns an error: %v", err)
 	}
@@ -1115,7 +1096,6 @@ func TestSetYouTrackService(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/services/youtrack", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "properties": {}}`)
 	})
 
 	opt := &SetYouTrackServiceOptions{
@@ -1125,7 +1105,7 @@ func TestSetYouTrackService(t *testing.T) {
 		PushEvents:  Ptr(true),
 	}
 
-	_, _, err := client.Services.SetYouTrackService(1, opt)
+	_, err := client.Services.SetYouTrackService(1, opt)
 	if err != nil {
 		t.Fatalf("Services.SetYouTrackService returns an error: %v", err)
 	}