Repository: xanzy/go-gitlab
Pull request #1350 (2022-01-25 15:12:47)

Add support for webhook subgroup events
Docs are 100% up-to-date with the current payload: https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#subgroup-events. I also updated all links to the other webhook events since they have been moved.

I tried to look at existing capitalizations of SubGroup and align it with those, hope it's okay


Patch:
diff --git a/client_options.go b/client_options.go
index 2ba5424..f1d39ad 100644
--- a/client_options.go
+++ b/client_options.go
@@ -83,22 +83,6 @@ func WithHTTPClient(httpClient *http.Client) ClientOptionFunc {
 	}
 }
 
-// WithRequestLogHook can be used to configure a custom request log hook.
-func WithRequestLogHook(hook retryablehttp.RequestLogHook) ClientOptionFunc {
-	return func(c *Client) error {
-		c.client.RequestLogHook = hook
-		return nil
-	}
-}
-
-// WithResponseLogHook can be used to configure a custom response log hook.
-func WithResponseLogHook(hook retryablehttp.ResponseLogHook) ClientOptionFunc {
-	return func(c *Client) error {
-		c.client.ResponseLogHook = hook
-		return nil
-	}
-}
-
 // WithoutRetries disables the default retry logic.
 func WithoutRetries() ClientOptionFunc {
 	return func(c *Client) error {
diff --git a/epics.go b/epics.go
index cbd6b11..825f24f 100644
--- a/epics.go
+++ b/epics.go
@@ -63,7 +63,6 @@ type Epic struct {
 	DueDateFromMilestones   *ISOTime    `json:"due_date_from_milestones"`
 	CreatedAt               *time.Time  `json:"created_at"`
 	UpdatedAt               *time.Time  `json:"updated_at"`
-	ClosedAt                *time.Time  `json:"closed_at"`
 	Labels                  []string    `json:"labels"`
 	Upvotes                 int         `json:"upvotes"`
 	Downvotes               int         `json:"downvotes"`
diff --git a/event_parsing.go b/event_parsing.go
index 69259e0..e17a0f1 100644
--- a/event_parsing.go
+++ b/event_parsing.go
@@ -39,6 +39,7 @@ const (
 	EventTypePush          EventType = "Push Hook"
 	EventTypeRelease       EventType = "Release Hook"
 	EventTypeServiceHook   EventType = "Service Hook"
+	EventTypeSubGroup      EventType = "Subgroup Hook"
 	EventTypeSystemHook    EventType = "System Hook"
 	EventTypeTagPush       EventType = "Tag Push Hook"
 	EventTypeWikiPage      EventType = "Wiki Page Hook"
@@ -265,6 +266,8 @@ func ParseWebhook(eventType EventType, payload []byte) (event interface{}, err e
 		default:
 			return nil, fmt.Errorf("unexpected service type %s", service.ObjectKind)
 		}
+	case EventTypeSubGroup:
+		event = &SubGroupEvent{}
 	case EventTypeTagPush:
 		event = &TagEvent{}
 	case EventTypeWikiPage:
diff --git a/event_webhook_types.go b/event_webhook_types.go
index 1a14052..cbdcd23 100644
--- a/event_webhook_types.go
+++ b/event_webhook_types.go
@@ -61,7 +61,7 @@ type BuildEvent struct {
 // CommitCommentEvent represents a comment on a commit event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#comment-on-commit
+// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#comment-on-a-commit
 type CommitCommentEvent struct {
 	ObjectKind string `json:"object_kind"`
 	User       *User  `json:"user"`
@@ -125,7 +125,7 @@ type CommitCommentEvent struct {
 // DeploymentEvent represents a deployment event
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#deployment-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#deployment-events
 type DeploymentEvent struct {
 	ObjectKind    string `json:"object_kind"`
 	Status        string `json:"status"`
@@ -160,7 +160,7 @@ type DeploymentEvent struct {
 // IssueCommentEvent represents a comment on an issue event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#comment-on-issue
+// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#comment-on-an-issue
 type IssueCommentEvent struct {
 	ObjectKind string `json:"object_kind"`
 	User       *User  `json:"user"`
@@ -230,7 +230,7 @@ type IssueCommentEvent struct {
 // IssueEvent represents a issue event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#issues-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#issue-events
 type IssueEvent struct {
 	ObjectKind string     `json:"object_kind"`
 	User       *EventUser `json:"user"`
@@ -344,7 +344,7 @@ type JobEvent struct {
 // MergeCommentEvent represents a comment on a merge event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#comment-on-merge-request
+// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-on-a-merge-request
 type MergeCommentEvent struct {
 	ObjectKind string     `json:"object_kind"`
 	User       *EventUser `json:"user"`
@@ -447,7 +447,7 @@ type MergeCommentEvent struct {
 // MergeEvent represents a merge event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#merge-request-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#merge-request-events
 type MergeEvent struct {
 	ObjectKind string     `json:"object_kind"`
 	User       *EventUser `json:"user"`
@@ -623,7 +623,7 @@ func (p *MergeParams) UnmarshalJSON(b []byte) error {
 // PipelineEvent represents a pipeline event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#pipeline-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#pipeline-events
 type PipelineEvent struct {
 	ObjectKind       string `json:"object_kind"`
 	ObjectAttributes struct {
@@ -720,7 +720,7 @@ type PipelineEvent struct {
 // PushEvent represents a push event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#push-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#push-events
 type PushEvent struct {
 	ObjectKind   string `json:"object_kind"`
 	Before       string `json:"before"`
@@ -770,7 +770,7 @@ type PushEvent struct {
 // ReleaseEvent represents a release event
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#release-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#release-events
 type ReleaseEvent struct {
 	ID          int    `json:"id"`
 	CreatedAt   string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
@@ -829,7 +829,7 @@ type ReleaseEvent struct {
 // SnippetCommentEvent represents a comment on a snippet event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#comment-on-code-snippet
+// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-on-a-code-snippet
 type SnippetCommentEvent struct {
 	ObjectKind string     `json:"object_kind"`
 	User       *EventUser `json:"user"`
@@ -871,10 +871,28 @@ type SnippetCommentEvent struct {
 	Snippet *Snippet `json:"snippet"`
 }
 
+// SubGroupEvent represents a subgroup event.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#subgroup-events
+type SubGroupEvent struct {
+	CreatedAt      *time.Time `json:"created_at"`
+	UpdatedAt      *time.Time `json:"updated_at"`
+	EventName      string     `json:"event_name"`
+	Name           string     `json:"name"`
+	Path           string     `json:"path"`
+	FullPath       string     `json:"full_path"`
+	GroupID        int        `json:"group_id"`
+	ParentGroupID  int        `json:"parent_group_id"`
+	ParentName     string     `json:"parent_name"`
+	ParentPath     string     `json:"parent_path"`
+	ParentFullPath string     `json:"parent_full_path"`
+}
+
 // TagEvent represents a tag event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#tag-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#tag-events
 type TagEvent struct {
 	ObjectKind   string `json:"object_kind"`
 	Before       string `json:"before"`
@@ -925,7 +943,7 @@ type TagEvent struct {
 // WikiPageEvent represents a wiki page event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#wiki-page-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#wiki-page-events
 type WikiPageEvent struct {
 	ObjectKind string     `json:"object_kind"`
 	User       *EventUser `json:"user"`
diff --git a/gitlab.go b/gitlab.go
index 697e21d..7c62777 100644
--- a/gitlab.go
+++ b/gitlab.go
@@ -127,12 +127,10 @@ type Client struct {
 	GenericPackages       *GenericPackagesService
 	GeoNodes              *GeoNodesService
 	GitIgnoreTemplates    *GitIgnoreTemplatesService
-	GroupAccessTokens     *GroupAccessTokensService
 	GroupBadges           *GroupBadgesService
 	GroupCluster          *GroupClustersService
 	GroupImportExport     *GroupImportExportService
 	GroupIssueBoards      *GroupIssueBoardsService
-	GroupIterations       *GroupIterationsService
 	GroupLabels           *GroupLabelsService
 	GroupMembers          *GroupMembersService
 	GroupMilestones       *GroupMilestonesService
@@ -169,7 +167,6 @@ type Client struct {
 	ProjectAccessTokens   *ProjectAccessTokensService
 	ProjectCluster        *ProjectClustersService
 	ProjectImportExport   *ProjectImportExportService
-	ProjectIterations     *ProjectIterationsService
 	ProjectMembers        *ProjectMembersService
 	ProjectMirrors        *ProjectMirrorService
 	ProjectSnippets       *ProjectSnippetsService
@@ -324,12 +321,10 @@ func newClient(options ...ClientOptionFunc) (*Client, error) {
 	c.GenericPackages = &GenericPackagesService{client: c}
 	c.GeoNodes = &GeoNodesService{client: c}
 	c.GitIgnoreTemplates = &GitIgnoreTemplatesService{client: c}
-	c.GroupAccessTokens = &GroupAccessTokensService{client: c}
 	c.GroupBadges = &GroupBadgesService{client: c}
 	c.GroupCluster = &GroupClustersService{client: c}
 	c.GroupImportExport = &GroupImportExportService{client: c}
 	c.GroupIssueBoards = &GroupIssueBoardsService{client: c}
-	c.GroupIterations = &GroupIterationsService{client: c}
 	c.GroupLabels = &GroupLabelsService{client: c}
 	c.GroupMembers = &GroupMembersService{client: c}
 	c.GroupMilestones = &GroupMilestonesService{client: c}
@@ -366,7 +361,6 @@ func newClient(options ...ClientOptionFunc) (*Client, error) {
 	c.ProjectAccessTokens = &ProjectAccessTokensService{client: c}
 	c.ProjectCluster = &ProjectClustersService{client: c}
 	c.ProjectImportExport = &ProjectImportExportService{client: c}
-	c.ProjectIterations = &ProjectIterationsService{client: c}
 	c.ProjectMembers = &ProjectMembersService{client: c}
 	c.ProjectMirrors = &ProjectMirrorService{client: c}
 	c.ProjectSnippets = &ProjectSnippetsService{client: c}
diff --git a/group_access_tokens.go b/group_access_tokens.go
deleted file mode 100644
index 41532bf..0000000
--- a/group_access_tokens.go
+++ /dev/null
@@ -1,140 +0,0 @@
-//
-// Copyright 2022, Masahiro Yoshida
-//
-// 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 (
-	"fmt"
-	"net/http"
-	"time"
-)
-
-// GroupAccessTokensService handles communication with the
-// groups access tokens related methods of the GitLab API.
-//
-// GitLab API docs: https://docs.gitlab.com/ee/api/group_access_tokens.html
-type GroupAccessTokensService struct {
-	client *Client
-}
-
-// GroupAccessToken represents a GitLab Group Access Token.
-//
-// GitLab API docs: https://docs.gitlab.com/ee/api/group_access_tokens.html
-type GroupAccessToken struct {
-	ID          int              `json:"id"`
-	UserID      int              `json:"user_id"`
-	Name        string           `json:"name"`
-	Scopes      []string         `json:"scopes"`
-	CreatedAt   *time.Time       `json:"created_at"`
-	ExpiresAt   *ISOTime         `json:"expires_at"`
-	Active      bool             `json:"active"`
-	Revoked     bool             `json:"revoked"`
-	Token       string           `json:"token"`
-	AccessLevel AccessLevelValue `json:"access_level"`
-}
-
-func (v GroupAccessToken) String() string {
-	return Stringify(v)
-}
-
-// ListGroupAccessTokensOptions represents the available options for
-// listing variables in a group.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/group_access_tokens.html#list-group-access-tokens
-type ListGroupAccessTokensOptions ListOptions
-
-// ListGroupAccessTokens gets a list of all Group Access Tokens in a
-// group.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/group_access_tokens.html#list-group-access-tokens
-func (s *GroupAccessTokensService) ListGroupAccessTokens(gid interface{}, opt *ListGroupAccessTokensOptions, options ...RequestOptionFunc) ([]*GroupAccessToken, *Response, error) {
-	groups, err := parseID(gid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("groups/%s/access_tokens", PathEscape(groups))
-
-	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var gats []*GroupAccessToken
-	resp, err := s.client.Do(req, &gats)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return gats, resp, err
-}
-
-// CreateGroupAccessTokenOptions represents the available CreateVariable()
-// options.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/group_access_tokens.html#create-a-group-access-token
-type CreateGroupAccessTokenOptions struct {
-	Name        *string           `url:"name,omitempty" json:"name,omitempty"`
-	Scopes      *[]string         `url:"scopes,omitempty" json:"scopes,omitempty"`
-	AccessLevel *AccessLevelValue `url:"access_level,omitempty" json:"access_level,omitempty"`
-	ExpiresAt   *ISOTime          `url:"expires_at,omitempty" json:"expires_at,omitempty"`
-}
-
-// CreateGroupAccessToken creates a new Group Access Token.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/group_access_tokens.html#create-a-group-access-token
-func (s *GroupAccessTokensService) CreateGroupAccessToken(gid interface{}, opt *CreateGroupAccessTokenOptions, options ...RequestOptionFunc) (*GroupAccessToken, *Response, error) {
-	groups, err := parseID(gid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("groups/%s/access_tokens", PathEscape(groups))
-
-	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	pat := new(GroupAccessToken)
-	resp, err := s.client.Do(req, pat)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return pat, resp, err
-}
-
-// DeleteGroupAccessToken deletes a Group Access Token.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/group_access_tokens.html#revoke-a-group-access-token
-func (s *GroupAccessTokensService) DeleteGroupAccessToken(gid interface{}, id int, options ...RequestOptionFunc) (*Response, error) {
-	groups, err := parseID(gid)
-	if err != nil {
-		return nil, err
-	}
-	u := fmt.Sprintf("groups/%s/access_tokens/%d", PathEscape(groups), id)
-
-	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/group_hooks.go b/group_hooks.go
index 55c44f1..9339370 100644
--- a/group_hooks.go
+++ b/group_hooks.go
@@ -48,14 +48,14 @@ type GroupHook struct {
 // ListGroupHooks gets a list of group hooks.
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-group-hooks
-func (s *GroupsService) ListGroupHooks(gid interface{}, options ...RequestOptionFunc) ([]*GroupHook, *Response, error) {
+func (s *GroupsService) ListGroupHooks(gid interface{}) ([]*GroupHook, *Response, error) {
 	group, err := parseID(gid)
 	if err != nil {
 		return nil, nil, err
 	}
 	u := fmt.Sprintf("groups/%s/hooks", PathEscape(group))
 
-	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
+	req, err := s.client.NewRequest(http.MethodGet, u, nil, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/group_iterations.go b/group_iterations.go
deleted file mode 100644
index a642091..0000000
--- a/group_iterations.go
+++ /dev/null
@@ -1,90 +0,0 @@
-//
-// Copyright 2022, Daniel Steinke
-//
-// 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 (
-	"fmt"
-	"net/http"
-	"time"
-)
-
-// IterationsAPI handles communication with the iterations related methods
-// of the GitLab API
-//
-// GitLab API docs: https://docs.gitlab.com/ee/api/group_iterations.html
-type GroupIterationsService struct {
-	client *Client
-}
-
-// GroupInteration represents a GitLab iteration.
-//
-// GitLab API docs: https://docs.gitlab.com/ee/api/group_iterations.html
-type GroupIteration struct {
-	ID          int        `json:"id"`
-	IID         int        `json:"iid"`
-	Sequence    int        `json:"sequence"`
-	GroupID     int        `json:"group_id"`
-	Title       string     `json:"title"`
-	Description string     `json:"description"`
-	State       int        `json:"state"`
-	CreatedAt   *time.Time `json:"created_at"`
-	UpdatedAt   *time.Time `json:"updated_at"`
-	DueDate     *ISOTime   `json:"due_date"`
-	StartDate   *ISOTime   `json:"start_date"`
-	WebURL      string     `json:"web_url"`
-}
-
-func (i GroupIteration) String() string {
-	return Stringify(i)
-}
-
-// ListGroupIterationsOptions contains the available ListGroupIterations()
-// options
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/group_iterations.html#list-group-iterations
-type ListGroupIterationsOptions struct {
-	ListOptions
-	State            *string `url:"state,omitempty" json:"state,omitempty"`
-	Search           *string `url:"search,omitempty" json:"search,omitempty"`
-	IncludeAncestors *bool   `url:"include_ancestors,omitempty" json:"include_ancestors,omitempty"`
-}
-
-// ListGroupIterations returns a list of group iterations.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/group_iterations.html#list-group-iterations
-func (s *GroupIterationsService) ListGroupIterations(gid interface{}, opt *ListGroupIterationsOptions, options ...RequestOptionFunc) ([]*GroupIteration, *Response, error) {
-	group, err := parseID(gid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("groups/%s/iterations", PathEscape(group))
-
-	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var gis []*GroupIteration
-	resp, err := s.client.Do(req, &gis)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	return gis, resp, err
-}
diff --git a/group_labels.go b/group_labels.go
index 4c4f7c4..e46af3c 100644
--- a/group_labels.go
+++ b/group_labels.go
@@ -40,15 +40,8 @@ func (l GroupLabel) String() string {
 
 // ListGroupLabelsOptions represents the available ListGroupLabels() options.
 //
-// GitLab API docs: https://docs.gitlab.com/ee/api/group_labels.html#list-group-labels
-type ListGroupLabelsOptions struct {
-	ListOptions
-	WithCounts               *bool   `url:"with_counts,omitempty" json:"with_counts,omitempty"`
-	IncludeAncestorGroups    *bool   `url:"include_ancestor_groups,omitempty" json:"include_ancestor_groups,omitempty"`
-	IncludeDescendantGrouops *bool   `url:"include_descendant_groups,omitempty" json:"include_descendant_groups,omitempty"`
-	OnlyGroupLabels          *bool   `url:"only_group_labels,omitempty" json:"only_group_labels,omitempty"`
-	Search                   *string `url:"search,omitempty" json:"search,omitempty"`
-}
+// GitLab API docs: https://docs.gitlab.com/ce/api/labels.html#list-labels
+type ListGroupLabelsOptions ListOptions
 
 // ListGroupLabels gets all labels for given group.
 //
@@ -88,7 +81,7 @@ func (s *GroupLabelsService) GetGroupLabel(gid interface{}, labelID interface{},
 	if err != nil {
 		return nil, nil, err
 	}
-	u := fmt.Sprintf("groups/%s/labels/%s", PathEscape(group), PathEscape(label))
+	u := fmt.Sprintf("groups/%s/labels/%s", PathEscape(group), label)
 
 	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
 	if err != nil {
@@ -207,7 +200,7 @@ func (s *GroupLabelsService) SubscribeToGroupLabel(gid interface{}, labelID inte
 	if err != nil {
 		return nil, nil, err
 	}
-	u := fmt.Sprintf("groups/%s/labels/%s/subscribe", PathEscape(group), PathEscape(label))
+	u := fmt.Sprintf("groups/%s/labels/%s/subscribe", PathEscape(group), label)
 
 	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
 	if err != nil {
@@ -238,7 +231,7 @@ func (s *GroupLabelsService) UnsubscribeFromGroupLabel(gid interface{}, labelID
 	if err != nil {
 		return nil, err
 	}
-	u := fmt.Sprintf("groups/%s/labels/%s/unsubscribe", PathEscape(group), PathEscape(label))
+	u := fmt.Sprintf("groups/%s/labels/%s/unsubscribe", PathEscape(group), label)
 
 	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
 	if err != nil {
diff --git a/groups.go b/groups.go
index 2383ca8..8647a1c 100644
--- a/groups.go
+++ b/groups.go
@@ -114,125 +114,13 @@ func (s *GroupsService) ListGroups(opt *ListGroupsOptions, options ...RequestOpt
 		return nil, nil, err
 	}
 
-	var gs []*Group
-	resp, err := s.client.Do(req, &gs)
+	var g []*Group
+	resp, err := s.client.Do(req, &g)
 	if err != nil {
 		return nil, resp, err
 	}
 
-	return gs, resp, err
-}
-
-// ListSubgroupsOptions represents the available ListSubgroups() options.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-s-subgroups
-type ListSubgroupsOptions ListGroupsOptions
-
-// ListSubgroups gets a list of subgroups for a given group.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-s-subgroups
-func (s *GroupsService) ListSubgroups(gid interface{}, opt *ListSubgroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) {
-	group, err := parseID(gid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("groups/%s/subgroups", PathEscape(group))
-
-	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var gs []*Group
-	resp, err := s.client.Do(req, &gs)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return gs, resp, err
-}
-
-// ListDescendantGroupsOptions represents the available ListDescendantGroups()
-// options.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-descendant-groups
-type ListDescendantGroupsOptions ListGroupsOptions
-
-// ListDescendantGroups gets a list of subgroups for a given project.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-descendant-groups
-func (s *GroupsService) ListDescendantGroups(gid interface{}, opt *ListDescendantGroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) {
-	group, err := parseID(gid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("groups/%s/descendant_groups", PathEscape(group))
-
-	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var gs []*Group
-	resp, err := s.client.Do(req, &gs)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return gs, resp, err
-}
-
-// ListGroupProjectsOptions represents the available ListGroup() options.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ce/api/groups.html#list-a-group-39-s-projects
-type ListGroupProjectsOptions struct {
-	ListOptions
-	Archived                 *bool             `url:"archived,omitempty" json:"archived,omitempty"`
-	IncludeSubgroups         *bool             `url:"include_subgroups,omitempty" json:"include_subgroups,omitempty"`
-	MinAccessLevel           *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"`
-	OrderBy                  *string           `url:"order_by,omitempty" json:"order_by,omitempty"`
-	Owned                    *bool             `url:"owned,omitempty" json:"owned,omitempty"`
-	Search                   *string           `url:"search,omitempty" json:"search,omitempty"`
-	Simple                   *bool             `url:"simple,omitempty" json:"simple,omitempty"`
-	Sort                     *string           `url:"sort,omitempty" json:"sort,omitempty"`
-	Starred                  *bool             `url:"starred,omitempty" json:"starred,omitempty"`
-	Topic                    *string           `url:"topic,omitempty" json:"topic,omitempty"`
-	Visibility               *VisibilityValue  `url:"visibility,omitempty" json:"visibility,omitempty"`
-	WithCustomAttributes     *bool             `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
-	WithIssuesEnabled        *bool             `url:"with_issues_enabled,omitempty" json:"with_issues_enabled,omitempty"`
-	WithMergeRequestsEnabled *bool             `url:"with_merge_requests_enabled,omitempty" json:"with_merge_requests_enabled,omitempty"`
-	WithSecurityReports      *bool             `url:"with_security_reports,omitempty" json:"with_security_reports,omitempty"`
-	WithShared               *bool             `url:"with_shared,omitempty" json:"with_shared,omitempty"`
-}
-
-// ListGroupProjects get a list of group projects
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ce/api/groups.html#list-a-group-39-s-projects
-func (s *GroupsService) ListGroupProjects(gid interface{}, opt *ListGroupProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) {
-	group, err := parseID(gid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("groups/%s/projects", PathEscape(group))
-
-	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var ps []*Project
-	resp, err := s.client.Do(req, &ps)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return ps, resp, err
+	return g, resp, err
 }
 
 // GetGroupOptions represents the available GetGroup() options.
@@ -452,53 +340,124 @@ func (s *GroupsService) SearchGroup(query string, options ...RequestOptionFunc)
 		return nil, nil, err
 	}
 
-	var gs []*Group
-	resp, err := s.client.Do(req, &gs)
+	var g []*Group
+	resp, err := s.client.Do(req, &g)
 	if err != nil {
 		return nil, resp, err
 	}
 
-	return gs, resp, err
+	return g, resp, err
 }
 
-// ListProvisionedUsersOptions represents the available ListProvisionedUsers()
-// options.
+// ListGroupProjectsOptions represents the available ListGroup() options.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/groups.html#provisioned-users-api
-type ListProvisionedUsersOptions struct {
+// https://docs.gitlab.com/ce/api/groups.html#list-a-group-39-s-projects
+type ListGroupProjectsOptions struct {
 	ListOptions
-	Username      *string    `url:"username,omitempty" json:"username,omitempty"`
-	Search        *string    `url:"search,omitempty" json:"search,omitempty"`
-	Active        *bool      `url:"active,omitempty" json:"active,omitempty"`
-	Blocked       *bool      `url:"blocked,omitempty" json:"blocked,omitempty"`
-	CreatedAfter  *time.Time `url:"created_after,omitempty" json:"created_after,omitempty"`
-	CreatedBefore *time.Time `url:"created_before,omitempty" json:"created_before,omitempty"`
+	Archived                 *bool             `url:"archived,omitempty" json:"archived,omitempty"`
+	IncludeSubgroups         *bool             `url:"include_subgroups,omitempty" json:"include_subgroups,omitempty"`
+	MinAccessLevel           *AccessLevelValue `url:"min_access_level,omitempty" json:"min_access_level,omitempty"`
+	OrderBy                  *string           `url:"order_by,omitempty" json:"order_by,omitempty"`
+	Owned                    *bool             `url:"owned,omitempty" json:"owned,omitempty"`
+	Search                   *string           `url:"search,omitempty" json:"search,omitempty"`
+	Simple                   *bool             `url:"simple,omitempty" json:"simple,omitempty"`
+	Sort                     *string           `url:"sort,omitempty" json:"sort,omitempty"`
+	Starred                  *bool             `url:"starred,omitempty" json:"starred,omitempty"`
+	Visibility               *VisibilityValue  `url:"visibility,omitempty" json:"visibility,omitempty"`
+	WithCustomAttributes     *bool             `url:"with_custom_attributes,omitempty" json:"with_custom_attributes,omitempty"`
+	WithIssuesEnabled        *bool             `url:"with_issues_enabled,omitempty" json:"with_issues_enabled,omitempty"`
+	WithMergeRequestsEnabled *bool             `url:"with_merge_requests_enabled,omitempty" json:"with_merge_requests_enabled,omitempty"`
+	WithSecurityReports      *bool             `url:"with_security_reports,omitempty" json:"with_security_reports,omitempty"`
+	WithShared               *bool             `url:"with_shared,omitempty" json:"with_shared,omitempty"`
 }
 
-// ListProvisionedUsers gets a list of users provisioned by the given group.
+// ListGroupProjects get a list of group projects
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/groups.html#provisioned-users-api
-func (s *GroupsService) ListProvisionedUsers(gid interface{}, opt *ListProvisionedUsersOptions, options ...RequestOptionFunc) ([]*User, *Response, error) {
+// https://docs.gitlab.com/ce/api/groups.html#list-a-group-39-s-projects
+func (s *GroupsService) ListGroupProjects(gid interface{}, opt *ListGroupProjectsOptions, options ...RequestOptionFunc) ([]*Project, *Response, error) {
 	group, err := parseID(gid)
 	if err != nil {
 		return nil, nil, err
 	}
-	u := fmt.Sprintf("groups/%s/provisioned_users", PathEscape(group))
+	u := fmt.Sprintf("groups/%s/projects", PathEscape(group))
 
 	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
 	if err != nil {
 		return nil, nil, err
 	}
 
-	var us []*User
-	resp, err := s.client.Do(req, &us)
+	var p []*Project
+	resp, err := s.client.Do(req, &p)
 	if err != nil {
 		return nil, resp, err
 	}
 
-	return us, resp, err
+	return p, resp, err
+}
+
+// ListSubgroupsOptions represents the available ListSubgroups() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-s-subgroups
+type ListSubgroupsOptions ListGroupsOptions
+
+// ListSubgroups gets a list of subgroups for a given group.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-s-subgroups
+func (s *GroupsService) ListSubgroups(gid interface{}, opt *ListSubgroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/subgroups", PathEscape(group))
+
+	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var g []*Group
+	resp, err := s.client.Do(req, &g)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return g, resp, err
+}
+
+// ListDescendantGroupsOptions represents the available ListDescendantGroups()
+// options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-descendant-groups
+type ListDescendantGroupsOptions ListGroupsOptions
+
+// ListDescendantGroups gets a list of subgroups for a given project.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ce/api/groups.html#list-a-groups-descendant-groups
+func (s *GroupsService) ListDescendantGroups(gid interface{}, opt *ListDescendantGroupsOptions, options ...RequestOptionFunc) ([]*Group, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/descendant_groups", PathEscape(group))
+
+	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var g []*Group
+	resp, err := s.client.Do(req, &g)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return g, resp, err
 }
 
 // ListGroupLDAPLinks lists the group's LDAP links. Available only for users who
@@ -518,13 +477,13 @@ func (s *GroupsService) ListGroupLDAPLinks(gid interface{}, options ...RequestOp
 		return nil, nil, err
 	}
 
-	var gls []*LDAPGroupLink
-	resp, err := s.client.Do(req, &gls)
+	var gl []*LDAPGroupLink
+	resp, err := s.client.Do(req, &gl)
 	if err != nil {
 		return nil, resp, err
 	}
 
-	return gls, resp, nil
+	return gl, resp, nil
 }
 
 // AddGroupLDAPLinkOptions represents the available AddGroupLDAPLink() options.
diff --git a/labels.go b/labels.go
index 66db474..b00ea1a 100644
--- a/labels.go
+++ b/labels.go
@@ -117,7 +117,7 @@ func (s *LabelsService) GetLabel(pid interface{}, labelID interface{}, options .
 	if err != nil {
 		return nil, nil, err
 	}
-	u := fmt.Sprintf("projects/%s/labels/%s", PathEscape(project), PathEscape(label))
+	u := fmt.Sprintf("projects/%s/labels/%s", PathEscape(project), label)
 
 	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
 	if err != nil {
@@ -242,7 +242,7 @@ func (s *LabelsService) SubscribeToLabel(pid interface{}, labelID interface{}, o
 	if err != nil {
 		return nil, nil, err
 	}
-	u := fmt.Sprintf("projects/%s/labels/%s/subscribe", PathEscape(project), PathEscape(label))
+	u := fmt.Sprintf("projects/%s/labels/%s/subscribe", PathEscape(project), label)
 
 	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
 	if err != nil {
@@ -273,7 +273,7 @@ func (s *LabelsService) UnsubscribeFromLabel(pid interface{}, labelID interface{
 	if err != nil {
 		return nil, err
 	}
-	u := fmt.Sprintf("projects/%s/labels/%s/unsubscribe", PathEscape(project), PathEscape(label))
+	u := fmt.Sprintf("projects/%s/labels/%s/unsubscribe", PathEscape(project), label)
 
 	req, err := s.client.NewRequest(http.MethodPost, u, nil, options)
 	if err != nil {
@@ -296,7 +296,7 @@ func (s *LabelsService) PromoteLabel(pid interface{}, labelID interface{}, optio
 	if err != nil {
 		return nil, err
 	}
-	u := fmt.Sprintf("projects/%s/labels/%s/promote", PathEscape(project), PathEscape(label))
+	u := fmt.Sprintf("projects/%s/labels/%s/promote", PathEscape(project), label)
 
 	req, err := s.client.NewRequest(http.MethodPut, u, nil, options)
 	if err != nil {
diff --git a/merge_requests.go b/merge_requests.go
index efbc8fe..2495924 100644
--- a/merge_requests.go
+++ b/merge_requests.go
@@ -54,7 +54,6 @@ type MergeRequest struct {
 	TargetProjectID           int              `json:"target_project_id"`
 	Labels                    Labels           `json:"labels"`
 	Description               string           `json:"description"`
-	Draft                     bool             `json:"draft"`
 	WorkInProgress            bool             `json:"work_in_progress"`
 	Milestone                 *Milestone       `json:"milestone"`
 	MergeWhenPipelineSucceeds bool             `json:"merge_when_pipeline_succeeds"`
@@ -524,14 +523,14 @@ func (s *MergeRequestsService) CreateMergeRequestPipeline(pid interface{}, merge
 // options.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/merge_requests.html#list-issues-that-close-on-merge
+// https://docs.gitlab.com/ce/api/merge_requests.html#list-issues-that-will-close-on-merge
 type GetIssuesClosedOnMergeOptions ListOptions
 
 // GetIssuesClosedOnMerge gets all the issues that would be closed by merging the
 // provided merge request.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/merge_requests.html#list-issues-that-close-on-merge
+// https://docs.gitlab.com/ce/api/merge_requests.html#list-issues-that-will-close-on-merge
 func (s *MergeRequestsService) GetIssuesClosedOnMerge(pid interface{}, mergeRequest int, opt *GetIssuesClosedOnMergeOptions, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
@@ -670,7 +669,7 @@ func (s *MergeRequestsService) DeleteMergeRequest(pid interface{}, mergeRequest
 // options.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/merge_requests.html#merge-a-merge-request
+// https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr
 type AcceptMergeRequestOptions struct {
 	MergeCommitMessage        *string `url:"merge_commit_message,omitempty" json:"merge_commit_message,omitempty"`
 	SquashCommitMessage       *string `url:"squash_commit_message,omitempty" json:"squash_commit_message,omitempty"`
@@ -686,7 +685,7 @@ type AcceptMergeRequestOptions struct {
 // already merged or closed - you get 405 and error message 'Method Not Allowed'
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/merge_requests.html#merge-a-merge-request
+// https://docs.gitlab.com/ce/api/merge_requests.html#accept-mr
 func (s *MergeRequestsService) AcceptMergeRequest(pid interface{}, mergeRequest int, opt *AcceptMergeRequestOptions, options ...RequestOptionFunc) (*MergeRequest, *Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
@@ -875,7 +874,7 @@ func (s *MergeRequestsService) UnsubscribeFromMergeRequest(pid interface{}, merg
 // status code 304 is returned.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/api/merge_requests.html#create-a-to-do-item
+// https://docs.gitlab.com/ce/api/merge_requests.html#create-a-todo
 func (s *MergeRequestsService) CreateTodo(pid interface{}, mergeRequest int, options ...RequestOptionFunc) (*Todo, *Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
diff --git a/notes.go b/notes.go
index 40e41d4..1b77d2a 100644
--- a/notes.go
+++ b/notes.go
@@ -68,8 +68,7 @@ type Note struct {
 		AvatarURL string `json:"avatar_url"`
 		WebURL    string `json:"web_url"`
 	} `json:"resolved_by"`
-	ResolvedAt  *time.Time `json:"resolved_at"`
-	NoteableIID int        `json:"noteable_iid"`
+	NoteableIID int `json:"noteable_iid"`
 }
 
 // NotePosition represents the position attributes of a note.
diff --git a/packages.go b/packages.go
index 37fe0e0..cb70ec7 100644
--- a/packages.go
+++ b/packages.go
@@ -30,7 +30,7 @@ type PackagesService struct {
 	client *Client
 }
 
-// Package represents a GitLab package.
+// Package represents a GitLab single package.
 //
 // GitLab API docs: https://docs.gitlab.com/ee/api/packages.html
 type Package struct {
@@ -38,29 +38,14 @@ type Package struct {
 	Name        string        `json:"name"`
 	Version     string        `json:"version"`
 	PackageType string        `json:"package_type"`
-	Status      string        `json:"status"`
 	Links       *PackageLinks `json:"_links"`
 	CreatedAt   *time.Time    `json:"created_at"`
-	Tags        []string      `json:"tags"`
 }
 
 func (s Package) String() string {
 	return Stringify(s)
 }
 
-// GroupPackage represents a GitLab group package.
-//
-// GitLab API docs: https://docs.gitlab.com/ee/api/packages.html
-type GroupPackage struct {
-	Package
-	ProjectID   int    `json:"project_id"`
-	ProjectPath string `json:"project_path"`
-}
-
-func (s GroupPackage) String() string {
-	return Stringify(s)
-}
-
 // PackageLinks holds links for itself and deleting.
 type PackageLinks struct {
 	WebPath       string `json:"web_path"`
@@ -89,8 +74,7 @@ func (s PackageFile) String() string {
 	return Stringify(s)
 }
 
-// ListProjectPackagesOptions represents the available ListProjectPackages()
-// options.
+// ListProjectPackagesOptions are the parameters available in a ListProjectPackages() Operation.
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/packages.html#within-a-project
@@ -101,7 +85,6 @@ type ListProjectPackagesOptions struct {
 	PackageType        *string `url:"package_type,omitempty" json:"package_type,omitempty"`
 	PackageName        *string `url:"package_name,omitempty" json:"package_name,omitempty"`
 	IncludeVersionless *bool   `url:"include_versionless,omitempty" json:"include_versionless,omitempty"`
-	Status             *string `url:"status,omitempty" json:"status,omitempty"`
 }
 
 // ListProjectPackages gets a list of packages in a project.
@@ -129,49 +112,8 @@ func (s *PackagesService) ListProjectPackages(pid interface{}, opt *ListProjectP
 	return ps, resp, err
 }
 
-// ListGroupPackagesOptions represents the available ListGroupPackages()
-// options.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/packages.html#within-a-group
-type ListGroupPackagesOptions struct {
-	ListOptions
-	ExcludeSubgroups   *bool   `url:"exclude_subgroups,omitempty" json:"exclude_subgroups,omitempty"`
-	OrderBy            *string `url:"order_by,omitempty" json:"order_by,omitempty"`
-	Sort               *string `url:"sort,omitempty" json:"sort,omitempty"`
-	PackageType        *string `url:"package_type,omitempty" json:"package_type,omitempty"`
-	PackageName        *string `url:"package_name,omitempty" json:"package_name,omitempty"`
-	IncludeVersionless *bool   `url:"include_versionless,omitempty" json:"include_versionless,omitempty"`
-	Status             *string `url:"status,omitempty" json:"status,omitempty"`
-}
-
-// ListGroupPackages gets a list of packages in a group.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/packages.html#within-a-group
-func (s *PackagesService) ListGroupPackages(gid interface{}, opt *ListGroupPackagesOptions, options ...RequestOptionFunc) ([]*GroupPackage, *Response, error) {
-	group, err := parseID(gid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("groups/%s/packages", PathEscape(group))
-
-	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var ps []*GroupPackage
-	resp, err := s.client.Do(req, &ps)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return ps, resp, err
-}
-
-// ListPackageFilesOptions represents the available ListPackageFiles()
-// options.
+// ListPackageFilesOptions represents the available
+// ListPackageFiles() options.
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/packages.html#list-package-files
diff --git a/pipelines.go b/pipelines.go
index 9273202..524d9a9 100644
--- a/pipelines.go
+++ b/pipelines.go
@@ -46,7 +46,6 @@ type Pipeline struct {
 	ID             int             `json:"id"`
 	ProjectID      int             `json:"project_id"`
 	Status         string          `json:"status"`
-	Source         string          `json:"source"`
 	Ref            string          `json:"ref"`
 	SHA            string          `json:"sha"`
 	BeforeSHA      string          `json:"before_sha"`
@@ -136,7 +135,6 @@ type PipelineInfo struct {
 	ID        int        `json:"id"`
 	ProjectID int        `json:"project_id"`
 	Status    string     `json:"status"`
-	Source    string     `json:"source"`
 	Ref       string     `json:"ref"`
 	SHA       string     `json:"sha"`
 	WebURL    string     `json:"web_url"`
@@ -155,7 +153,6 @@ type ListProjectPipelinesOptions struct {
 	ListOptions
 	Scope         *string          `url:"scope,omitempty" json:"scope,omitempty"`
 	Status        *BuildStateValue `url:"status,omitempty" json:"status,omitempty"`
-	Source        *string          `url:"source,omitempty" json:"source,omitempty"`
 	Ref           *string          `url:"ref,omitempty" json:"ref,omitempty"`
 	SHA           *string          `url:"sha,omitempty" json:"sha,omitempty"`
 	YamlErrors    *bool            `url:"yaml_errors,omitempty" json:"yaml_errors,omitempty"`
@@ -242,14 +239,14 @@ func (s *PipelinesService) GetPipelineVariables(pid interface{}, pipeline int, o
 // GetPipelineTestReport gets the test report of a single project pipeline.
 //
 // GitLab API docs: https://docs.gitlab.com/ee/api/pipelines.html#get-a-pipelines-test-report
-func (s *PipelinesService) GetPipelineTestReport(pid interface{}, pipeline int, options ...RequestOptionFunc) (*PipelineTestReport, *Response, error) {
+func (s *PipelinesService) GetPipelineTestReport(pid interface{}, pipeline int) (*PipelineTestReport, *Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
 		return nil, nil, err
 	}
 	u := fmt.Sprintf("projects/%s/pipelines/%d/test_report", PathEscape(project), pipeline)
 
-	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
+	req, err := s.client.NewRequest(http.MethodGet, u, nil, nil)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/project_badges.go b/project_badges.go
index aa62a1f..2fce3e0 100644
--- a/project_badges.go
+++ b/project_badges.go
@@ -27,7 +27,6 @@ import (
 // https://docs.gitlab.com/ee/api/project_badges.html#list-all-badges-of-a-project
 type ProjectBadge struct {
 	ID               int    `json:"id"`
-	Name             string `json:"name"`
 	LinkURL          string `json:"link_url"`
 	ImageURL         string `json:"image_url"`
 	RenderedLinkURL  string `json:"rendered_link_url"`
@@ -49,10 +48,7 @@ type ProjectBadgesService struct {
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/project_badges.html#list-all-badges-of-a-project
-type ListProjectBadgesOptions struct {
-	ListOptions
-	Name *string `url:"name,omitempty" json:"name,omitempty"`
-}
+type ListProjectBadgesOptions ListOptions
 
 // ListProjectBadges gets a list of a project's badges and its group badges.
 //
@@ -111,7 +107,6 @@ func (s *ProjectBadgesService) GetProjectBadge(pid interface{}, badge int, optio
 type AddProjectBadgeOptions struct {
 	LinkURL  *string `url:"link_url,omitempty" json:"link_url,omitempty"`
 	ImageURL *string `url:"image_url,omitempty" json:"image_url,omitempty"`
-	Name     *string `url:"name,omitempty" json:"name,omitempty"`
 }
 
 // AddProjectBadge adds a badge to a project.
@@ -146,7 +141,6 @@ func (s *ProjectBadgesService) AddProjectBadge(pid interface{}, opt *AddProjectB
 type EditProjectBadgeOptions struct {
 	LinkURL  *string `url:"link_url,omitempty" json:"link_url,omitempty"`
 	ImageURL *string `url:"image_url,omitempty" json:"image_url,omitempty"`
-	Name     *string `url:"name,omitempty" json:"name,omitempty"`
 }
 
 // EditProjectBadge updates a badge of a project.
diff --git a/project_iterations.go b/project_iterations.go
deleted file mode 100644
index 78583ef..0000000
--- a/project_iterations.go
+++ /dev/null
@@ -1,90 +0,0 @@
-//
-// Copyright 2022, Daniel Steinke
-//
-// 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 (
-	"fmt"
-	"net/http"
-	"time"
-)
-
-// IterationsAPI handles communication with the project iterations related
-// methods of the GitLab API
-//
-// GitLab API docs: https://docs.gitlab.com/ee/api/iterations.html
-type ProjectIterationsService struct {
-	client *Client
-}
-
-// ProjectIteration represents a GitLab project iteration.
-//
-// GitLab API docs: https://docs.gitlab.com/ee/api/iterations.html
-type ProjectIteration struct {
-	ID          int        `json:"id"`
-	IID         int        `json:"iid"`
-	Sequence    int        `json:"sequence"`
-	GroupID     int        `json:"group_id"`
-	Title       string     `json:"title"`
-	Description string     `json:"description"`
-	State       int        `json:"state"`
-	CreatedAt   *time.Time `json:"created_at"`
-	UpdatedAt   *time.Time `json:"updated_at"`
-	DueDate     *ISOTime   `json:"due_date"`
-	StartDate   *ISOTime   `json:"start_date"`
-	WebURL      string     `json:"web_url"`
-}
-
-func (i ProjectIteration) String() string {
-	return Stringify(i)
-}
-
-// ListProjectIterationsOptions contains the available ListProjectIterations()
-// options
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/group_iterations.html#list-project-iterations
-type ListProjectIterationsOptions struct {
-	ListOptions
-	State            *string `url:"state,omitempty" json:"state,omitempty"`
-	Search           *string `url:"search,omitempty" json:"search,omitempty"`
-	IncludeAncestors *bool   `url:"include_ancestors,omitempty" json:"include_ancestors,omitempty"`
-}
-
-// ListProjectIterations returns a list of projects iterations.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/group_iterations.html#list-project-iterations
-func (i *ProjectIterationsService) ListProjectIterations(pid interface{}, opt *ListProjectIterationsOptions, options ...RequestOptionFunc) ([]*ProjectIteration, *Response, error) {
-	project, err := parseID(pid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("projects/%s/iterations", PathEscape(project))
-
-	req, err := i.client.NewRequest(http.MethodGet, u, opt, options)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	var pis []*ProjectIteration
-	resp, err := i.client.Do(req, &pis)
-	if err != nil {
-		return nil, resp, err
-	}
-
-	return pis, resp, err
-}
diff --git a/project_snippets.go b/project_snippets.go
index 82db719..6bbaeaf 100644
--- a/project_snippets.go
+++ b/project_snippets.go
@@ -94,7 +94,6 @@ type CreateProjectSnippetOptions struct {
 	Description *string          `url:"description,omitempty" json:"description,omitempty"`
 	Content     *string          `url:"content,omitempty" json:"content,omitempty"`
 	Visibility  *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
-	Files       *[]*SnippetFile  `url:"files,omitempty" json:"files,omitempty"`
 }
 
 // CreateSnippet creates a new project snippet. The user must have permission
diff --git a/project_variables.go b/project_variables.go
index 5f121ff..2c4d4e9 100644
--- a/project_variables.go
+++ b/project_variables.go
@@ -48,11 +48,6 @@ func (v ProjectVariable) String() string {
 	return Stringify(v)
 }
 
-//VariableFilter filters available for project variable related functions
-type VariableFilter struct {
-	EnvironmentScope string `url:"environment_scope, omitempty" json:"environment_scope,omitempty"`
-}
-
 // ListProjectVariablesOptions represents the available options for listing variables
 // in a project.
 //
@@ -85,27 +80,18 @@ func (s *ProjectVariablesService) ListVariables(pid interface{}, opt *ListProjec
 	return vs, resp, err
 }
 
-// GetProjectVariableOptions represents the available GetVariable()
-// options.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/project_level_variables.html#show-variable-details
-type GetProjectVariableOptions struct {
-	Filter *VariableFilter `url:"filter,omitempty" json:"filter,omitempty"`
-}
-
 // GetVariable gets a variable.
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/project_level_variables.html#show-variable-details
-func (s *ProjectVariablesService) GetVariable(pid interface{}, key string, opt *GetProjectVariableOptions, options ...RequestOptionFunc) (*ProjectVariable, *Response, error) {
+func (s *ProjectVariablesService) GetVariable(pid interface{}, key string, options ...RequestOptionFunc) (*ProjectVariable, *Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
 		return nil, nil, err
 	}
 	u := fmt.Sprintf("projects/%s/variables/%s", PathEscape(project), url.PathEscape(key))
 
-	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
+	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -169,7 +155,6 @@ type UpdateProjectVariableOptions struct {
 	Protected        *bool              `url:"protected,omitempty" json:"protected,omitempty"`
 	Masked           *bool              `url:"masked,omitempty" json:"masked,omitempty"`
 	EnvironmentScope *string            `url:"environment_scope,omitempty" json:"environment_scope,omitempty"`
-	Filter           *VariableFilter    `url:"filter,omitempty" json:"filter,omitempty"`
 }
 
 // UpdateVariable updates a project's variable.
@@ -197,27 +182,18 @@ func (s *ProjectVariablesService) UpdateVariable(pid interface{}, key string, op
 	return v, resp, err
 }
 
-// RemoveProjectVariableOptions represents the available RemoveVariable()
-// options.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/project_level_variables.html#remove-variable
-type RemoveProjectVariableOptions struct {
-	Filter *VariableFilter `url:"filter,omitempty" json:"filter,omitempty"`
-}
-
 // RemoveVariable removes a project's variable.
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/project_level_variables.html#remove-variable
-func (s *ProjectVariablesService) RemoveVariable(pid interface{}, key string, opt *RemoveProjectVariableOptions, options ...RequestOptionFunc) (*Response, error) {
+func (s *ProjectVariablesService) RemoveVariable(pid interface{}, key string, options ...RequestOptionFunc) (*Response, error) {
 	project, err := parseID(pid)
 	if err != nil {
 		return nil, err
 	}
 	u := fmt.Sprintf("projects/%s/variables/%s", PathEscape(project), url.PathEscape(key))
 
-	req, err := s.client.NewRequest(http.MethodDelete, u, opt, options)
+	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
 	if err != nil {
 		return nil, err
 	}
diff --git a/projects.go b/projects.go
index baf3b31..86ed609 100644
--- a/projects.go
+++ b/projects.go
@@ -127,7 +127,6 @@ type Project struct {
 	CustomAttributes             []*CustomAttribute `json:"custom_attributes"`
 	ComplianceFrameworks         []string           `json:"compliance_frameworks"`
 	BuildCoverageRegex           string             `json:"build_coverage_regex"`
-	BuildTimeout                 int                `json:"build_timeout"`
 	IssuesTemplate               string             `json:"issues_template"`
 	MergeRequestsTemplate        string             `json:"merge_requests_template"`
 	KeepLatestArtifact           bool               `json:"keep_latest_artifact"`
@@ -909,16 +908,9 @@ func (s *ProjectsService) EditProject(pid interface{}, opt *EditProjectOptions,
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#fork-project
 type ForkProjectOptions struct {
-	Description                   *string          `url:"description,omitempty" json:"description,omitempty"`
-	MergeRequestDefaultTargetSelf *bool            `url:"mr_default_target_self,omitempty" json:"mr_default_target_self,omitempty"`
-	Name                          *string          `url:"name,omitempty" json:"name,omitempty"`
-	NamespaceID                   *int             `url:"namespace_id,omitempty" json:"namespace_id,omitempty"`
-	NamespacePath                 *string          `url:"namespace_path,omitempty" json:"namespace_path,omitempty"`
-	Path                          *string          `url:"path,omitempty" json:"path,omitempty"`
-	Visibility                    *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
-
-	// Deprecated members
+	Name      *string `url:"name,omitempty" json:"name,omitempty" `
 	Namespace *string `url:"namespace,omitempty" json:"namespace,omitempty"`
+	Path      *string `url:"path,omitempty" json:"path,omitempty"`
 }
 
 // ForkProject forks a project into the user namespace of the authenticated
diff --git a/snippets.go b/snippets.go
index 26b8d4c..26468b7 100644
--- a/snippets.go
+++ b/snippets.go
@@ -51,10 +51,6 @@ type Snippet struct {
 	CreatedAt *time.Time `json:"created_at"`
 	WebURL    string     `json:"web_url"`
 	RawURL    string     `json:"raw_url"`
-	Files     []struct {
-		Path   string `json:"path"`
-		RawURL string `json:"raw_url"`
-	} `json:"files"`
 }
 
 func (s Snippet) String() string {
@@ -105,15 +101,6 @@ func (s *SnippetsService) GetSnippet(snippet int, options ...RequestOptionFunc)
 	return ps, resp, err
 }
 
-// SnippetFile represents the object that is used to create snippets
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ce/api/snippets.html#create-new-snippet
-type SnippetFile struct {
-	FilePath *string `url:"file_path,omitempty" json:"file_path,omitempty"`
-	Content  *string `url:"content,omitempty" json:"content,omitempty"`
-}
-
 // CreateSnippetOptions represents the available CreateSnippet() options.
 //
 // GitLab API docs:
@@ -124,7 +111,6 @@ type CreateSnippetOptions struct {
 	Description *string          `url:"description,omitempty" json:"description,omitempty"`
 	Content     *string          `url:"content,omitempty" json:"content,omitempty"`
 	Visibility  *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
-	Files       *[]*SnippetFile  `url:"files,omitempty" json:"files,omitempty"`
 }
 
 // CreateSnippet creates a new snippet. The user must have permission
@@ -230,7 +216,7 @@ type ExploreSnippetsOptions ListOptions
 // GitLab API docs:
 // https://docs.gitlab.com/ce/api/snippets.html#explore-all-public-snippets
 func (s *SnippetsService) ExploreSnippets(opt *ExploreSnippetsOptions, options ...RequestOptionFunc) ([]*Snippet, *Response, error) {
-	req, err := s.client.NewRequest(http.MethodGet, "snippets/public", opt, options)
+	req, err := s.client.NewRequest(http.MethodGet, "snippets/public", nil, options)
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/topics.go b/topics.go
index b227e8b..04f4473 100644
--- a/topics.go
+++ b/topics.go
@@ -150,18 +150,3 @@ func (s *TopicsService) UpdateTopic(topic int, opt *UpdateTopicOptions, options
 
 	return t, resp, err
 }
-
-// DeleteTopic deletes a project topic. Only available to administrators.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/topics.html#delete-a-project-topic
-func (s *TopicsService) DeleteTopic(topic int, options ...RequestOptionFunc) (*Response, error) {
-	u := fmt.Sprintf("topics/%d", topic)
-
-	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/users.go b/users.go
index c16b07e..4b4e8c3 100644
--- a/users.go
+++ b/users.go
@@ -396,7 +396,6 @@ type SSHKey struct {
 	Title     string     `json:"title"`
 	Key       string     `json:"key"`
 	CreatedAt *time.Time `json:"created_at"`
-	ExpiresAt *time.Time `json:"expires_at"`
 }
 
 // ListSSHKeys gets a list of currently authenticated user's SSH keys.
@@ -423,16 +422,13 @@ func (s *UsersService) ListSSHKeys(options ...RequestOptionFunc) ([]*SSHKey, *Re
 // https://docs.gitlab.com/ce/api/users.html#list-ssh-keys-for-user
 type ListSSHKeysForUserOptions ListOptions
 
-// ListSSHKeysForUser gets a list of a specified user's SSH keys.
+// ListSSHKeysForUser gets a list of a specified user's SSH keys. Available
+// only for admin
 //
 // GitLab API docs:
 // https://docs.gitlab.com/ce/api/users.html#list-ssh-keys-for-user
-func (s *UsersService) ListSSHKeysForUser(uid interface{}, opt *ListSSHKeysForUserOptions, options ...RequestOptionFunc) ([]*SSHKey, *Response, error) {
-	user, err := parseID(uid)
-	if err != nil {
-		return nil, nil, err
-	}
-	u := fmt.Sprintf("users/%s/keys", user)
+func (s *UsersService) ListSSHKeysForUser(user int, opt *ListSSHKeysForUserOptions, options ...RequestOptionFunc) ([]*SSHKey, *Response, error) {
+	u := fmt.Sprintf("users/%d/keys", user)
 
 	req, err := s.client.NewRequest(http.MethodGet, u, opt, options)
 	if err != nil {


Test patch:
diff --git a/event_parsing_webhook_test.go b/event_parsing_webhook_test.go
index cf582d5..b991deb 100644
--- a/event_parsing_webhook_test.go
+++ b/event_parsing_webhook_test.go
@@ -387,6 +387,24 @@ func TestParseSnippetCommentHook(t *testing.T) {
 	}
 }
 
+func TestParseSubGroupHook(t *testing.T) {
+	raw := loadFixture("testdata/webhooks/subgroup.json")
+
+	parsedEvent, err := ParseWebhook("Subgroup Hook", raw)
+	if err != nil {
+		t.Errorf("Error parsing subgroup hook: %s", err)
+	}
+
+	event, ok := parsedEvent.(*SubGroupEvent)
+	if !ok {
+		t.Errorf("Expected SubGroupEvent, but parsing produced %T", parsedEvent)
+	}
+
+	if event.EventName != "subgroup_create" {
+		t.Errorf("EventName is %v, want %v", event.EventName, "subgroup_create")
+	}
+}
+
 func TestParseTagHook(t *testing.T) {
 	raw := loadFixture("testdata/webhooks/tag_push.json")
 
diff --git a/event_webhook_types_test.go b/event_webhook_types_test.go
index 9ba1bef..b0119da 100644
--- a/event_webhook_types_test.go
+++ b/event_webhook_types_test.go
@@ -19,6 +19,7 @@ package gitlab
 import (
 	"encoding/json"
 	"testing"
+	"time"
 )
 
 const (
@@ -339,7 +340,7 @@ func TestPipelineEventUnmarshal(t *testing.T) {
 	if event.Builds[0].AllowFailure != true {
 		t.Errorf("Builds.0.AllowFailure is %v, want %v", event.Builds[0].AllowFailure, true)
 	}
-	
+
 	if event.Builds[0].Environment.Name != "production" {
 		t.Errorf("Builds.0.Environment.Name is %v, want %v", event.Builds[0].Environment.Name, "production")
 	}
@@ -383,83 +384,114 @@ func TestPushEventUnmarshal(t *testing.T) {
 	}
 }
 
-func TestTagEventUnmarshal(t *testing.T) {
-	jsonObject := loadFixture("testdata/webhooks/tag_push.json")
-	var event *TagEvent
+func TestReleaseEventUnmarshal(t *testing.T) {
+	jsonObject := loadFixture("testdata/webhooks/release.json")
+
+	var event *ReleaseEvent
 	err := json.Unmarshal(jsonObject, &event)
 
 	if err != nil {
-		t.Errorf("Tag Event can not unmarshaled: %v\n ", err.Error())
+		t.Errorf("Release Event can not unmarshaled: %v\n ", err.Error())
 	}
 
 	if event == nil {
-		t.Errorf("Tag Event is null")
+		t.Errorf("Release Event is null")
 	}
 
-	if event.ProjectID != 1 {
-		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 1)
+	if event.Project.ID != 327622 {
+		t.Errorf("Project.ID is %v, want %v", event.Project.ID, 327622)
 	}
 
-	if event.UserName != exampleEventUserName {
-		t.Errorf("Username is %s, want %s", event.UserName, exampleEventUserName)
+	if event.Commit.Title != "Merge branch 'example-branch' into 'master'" {
+		t.Errorf("Commit title is %s, want %s", event.Commit.Title, "Merge branch 'example-branch' into 'master'")
 	}
 
-	if event.Commits[0] == nil || event.Commits[0].Timestamp == nil {
-		t.Errorf("Commit Timestamp isn't nil")
+	if len(event.Assets.Sources) != 4 {
+		t.Errorf("Asset sources length is %d, want %d", len(event.Assets.Sources), 4)
 	}
 
-	if event.Commits[0] == nil || event.Commits[0].Message != exampleCommitMessage {
-		t.Errorf("Commit Message is %s, want %s", event.Commits[0].Message, exampleCommitMessage)
+	if event.Assets.Sources[0].Format != "zip" {
+		t.Errorf("First asset source format is %s, want %s", event.Assets.Sources[0].Format, "zip")
 	}
 
-	if event.Commits[0] == nil || event.Commits[0].Title != exampleCommitTitle {
-		t.Errorf("Commit Title is %s, want %s", event.Commits[0].Title, exampleCommitTitle)
+	if len(event.Assets.Links) != 1 {
+		t.Errorf("Asset links length is %d, want %d", len(event.Assets.Links), 1)
 	}
 
-	if event.Commits[0] == nil || event.Commits[0].Author.Name != exampleEventUserName {
-		t.Errorf("Commit Username is %s, want %s", event.UserName, exampleEventUserName)
+	if event.Assets.Links[0].Name != "Changelog" {
+		t.Errorf("First asset link name is %s, want %s", event.Assets.Links[0].Name, "Changelog")
+	}
+
+	if event.Commit.Author.Name != "User" {
+		t.Errorf("Commit author name is %s, want %s", event.Commit.Author.Name, "User")
 	}
 }
 
-func TestReleaseEventUnmarshal(t *testing.T) {
-	jsonObject := loadFixture("testdata/webhooks/release.json")
+func TestSubGroupEventUnmarshal(t *testing.T) {
+	jsonObject := loadFixture("testdata/webhooks/subgroup.json")
 
-	var event *ReleaseEvent
+	var event *SubGroupEvent
 	err := json.Unmarshal(jsonObject, &event)
 
 	if err != nil {
-		t.Errorf("Release Event can not unmarshaled: %v\n ", err.Error())
+		t.Errorf("SubGroup Event can not unmarshaled: %v\n ", err.Error())
 	}
 
 	if event == nil {
-		t.Errorf("Release Event is null")
+		t.Errorf("SubGroup Event is null")
 	}
 
-	if event.Project.ID != 327622 {
-		t.Errorf("Project.ID is %v, want %v", event.Project.ID, 327622)
+	if event.Name != "Subgroup 1" {
+		t.Errorf("Name is %v, want %v", event.Name, "Subgroup 1")
 	}
 
-	if event.Commit.Title != "Merge branch 'example-branch' into 'master'" {
-		t.Errorf("Commit title is %s, want %s", event.Commit.Title, "Merge branch 'example-branch' into 'master'")
+	if event.GroupID != 2 {
+		t.Errorf("GroupID is %v, want %v", event.GroupID, 2)
 	}
 
-	if len(event.Assets.Sources) != 4 {
-		t.Errorf("Asset sources length is %d, want %d", len(event.Assets.Sources), 4)
+	if event.ParentGroupID != 1 {
+		t.Errorf("ParentGroupID is %v, want %v", event.ParentGroupID, 1)
 	}
 
-	if event.Assets.Sources[0].Format != "zip" {
-		t.Errorf("First asset source format is %s, want %s", event.Assets.Sources[0].Format, "zip")
+	if event.CreatedAt.Format(time.RFC3339) != "2022-01-24T14:23:59Z" {
+		t.Errorf("CreatedAt is %v, want %v", event.CreatedAt.Format(time.RFC3339), "2022-01-24T14:23:59Z")
 	}
+}
 
-	if len(event.Assets.Links) != 1 {
-		t.Errorf("Asset links length is %d, want %d", len(event.Assets.Links), 1)
+func TestTagEventUnmarshal(t *testing.T) {
+	jsonObject := loadFixture("testdata/webhooks/tag_push.json")
+	var event *TagEvent
+	err := json.Unmarshal(jsonObject, &event)
+
+	if err != nil {
+		t.Errorf("Tag Event can not unmarshaled: %v\n ", err.Error())
 	}
 
-	if event.Assets.Links[0].Name != "Changelog" {
-		t.Errorf("First asset link name is %s, want %s", event.Assets.Links[0].Name, "Changelog")
+	if event == nil {
+		t.Errorf("Tag Event is null")
 	}
 
-	if event.Commit.Author.Name != "User" {
-		t.Errorf("Commit author name is %s, want %s", event.Commit.Author.Name, "User")
+	if event.ProjectID != 1 {
+		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 1)
+	}
+
+	if event.UserName != exampleEventUserName {
+		t.Errorf("Username is %s, want %s", event.UserName, exampleEventUserName)
+	}
+
+	if event.Commits[0] == nil || event.Commits[0].Timestamp == nil {
+		t.Errorf("Commit Timestamp isn't nil")
+	}
+
+	if event.Commits[0] == nil || event.Commits[0].Message != exampleCommitMessage {
+		t.Errorf("Commit Message is %s, want %s", event.Commits[0].Message, exampleCommitMessage)
+	}
+
+	if event.Commits[0] == nil || event.Commits[0].Title != exampleCommitTitle {
+		t.Errorf("Commit Title is %s, want %s", event.Commits[0].Title, exampleCommitTitle)
+	}
+
+	if event.Commits[0] == nil || event.Commits[0].Author.Name != exampleEventUserName {
+		t.Errorf("Commit Username is %s, want %s", event.UserName, exampleEventUserName)
 	}
 }
diff --git a/group_access_tokens_test.go b/group_access_tokens_test.go
deleted file mode 100644
index 8b9be4f..0000000
--- a/group_access_tokens_test.go
+++ /dev/null
@@ -1,125 +0,0 @@
-//
-// Copyright 2022, Masahiro Yoshida
-//
-// 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 (
-	"net/http"
-	"reflect"
-	"testing"
-	"time"
-)
-
-func TestListGroupAccessTokens(t *testing.T) {
-	mux, server, client := setup(t)
-	defer teardown(server)
-
-	mux.HandleFunc("/api/v4/groups/1/access_tokens", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, http.MethodGet)
-		mustWriteHTTPResponse(t, w, "testdata/list_group_access_tokens.json")
-	})
-
-	groupAccessTokens, _, err := client.GroupAccessTokens.ListGroupAccessTokens(1, &ListGroupAccessTokensOptions{Page: 1, PerPage: 20})
-	if err != nil {
-		t.Errorf("GroupAccessTokens.ListGroupAccessTokens returned error: %v", err)
-	}
-
-	time1, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.271Z")
-	if err != nil {
-		t.Errorf("GroupAccessTokens.ListGroupAccessTokens returned error: %v", err)
-	}
-	time2, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.340Z")
-	if err != nil {
-		t.Errorf("GroupAccessTokens.ListGroupAccessTokens returned error: %v", err)
-	}
-
-	want := []*GroupAccessToken{
-		{
-			ID:          1876,
-			UserID:      2453,
-			Name:        "token 10",
-			Scopes:      []string{"api", "read_api", "read_repository", "write_repository"},
-			CreatedAt:   &time1,
-			Active:      true,
-			Revoked:     false,
-			AccessLevel: AccessLevelValue(40),
-		},
-		{
-			ID:          1877,
-			UserID:      2456,
-			Name:        "token 8",
-			Scopes:      []string{"api", "read_api", "read_repository", "write_repository"},
-			CreatedAt:   &time2,
-			Active:      true,
-			Revoked:     false,
-			AccessLevel: AccessLevelValue(30),
-		},
-	}
-
-	if !reflect.DeepEqual(want, groupAccessTokens) {
-		t.Errorf("GroupAccessTokens.ListGroupAccessTokens returned %+v, want %+v", groupAccessTokens, want)
-	}
-}
-
-func TestCreateGroupAccessToken(t *testing.T) {
-	mux, server, client := setup(t)
-	defer teardown(server)
-
-	mux.HandleFunc("/api/v4/groups/1/access_tokens", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, http.MethodPost)
-		mustWriteHTTPResponse(t, w, "testdata/create_group_access_token.json")
-	})
-
-	groupAccessToken, _, err := client.GroupAccessTokens.CreateGroupAccessToken(1, nil)
-	if err != nil {
-		t.Errorf("GroupAccessTokens.CreateGroupAccessToken returned error: %v", err)
-	}
-
-	time1, err := time.Parse(time.RFC3339, "2021-03-09T21:11:47.271Z")
-	if err != nil {
-		t.Errorf("GroupAccessTokens.CreateGroupAccessToken returned error: %v", err)
-	}
-	want := &GroupAccessToken{
-		ID:          1876,
-		UserID:      2453,
-		Name:        "token 10",
-		Scopes:      []string{"api", "read_api", "read_repository", "write_repository"},
-		ExpiresAt:   nil,
-		CreatedAt:   &time1,
-		Active:      true,
-		Revoked:     false,
-		Token:       "2UsevZE1x1ZdFZW4MNzH",
-		AccessLevel: AccessLevelValue(40),
-	}
-
-	if !reflect.DeepEqual(want, groupAccessToken) {
-		t.Errorf("GroupAccessTokens.CreateGroupAccessToken returned %+v, want %+v", groupAccessToken, want)
-	}
-}
-
-func TestDeleteGroupAccessToken(t *testing.T) {
-	mux, server, client := setup(t)
-	defer teardown(server)
-
-	mux.HandleFunc("/api/v4/groups/1/access_tokens/1234", func(w http.ResponseWriter, r *http.Request) {
-		testMethod(t, r, http.MethodDelete)
-	})
-
-	_, err := client.GroupAccessTokens.DeleteGroupAccessToken("1", 1234)
-	if err != nil {
-		t.Errorf("GroupAccessTokens.DeleteGroupAccessToken returned error: %v", err)
-	}
-}
diff --git a/group_iterations_test.go b/group_iterations_test.go
deleted file mode 100644
index 4d2030f..0000000
--- a/group_iterations_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package gitlab
-
-import (
-	"fmt"
-	"net/http"
-	"reflect"
-	"testing"
-)
-
-func TestListGroupIterations(t *testing.T) {
-	mux, server, client := setup(t)
-	defer teardown(server)
-
-	mux.HandleFunc("/api/v4/groups/5/iterations",
-		func(w http.ResponseWriter, r *http.Request) {
-			testMethod(t, r, http.MethodGet)
-			fmt.Fprintf(w, `[
-        {
-          "id": 53,
-          "iid": 13,
-          "sequence": 1,
-          "group_id": 5,
-          "title": "Iteration II",
-          "description": "Ipsum Lorem ipsum",
-          "state": 2,
-          "web_url": "http://gitlab.example.com/groups/my-group/-/iterations/13"
-        }
-      ]`)
-		})
-
-	iterations, _, err := client.GroupIterations.ListGroupIterations(5, &ListGroupIterationsOptions{})
-	if err != nil {
-		t.Errorf("GroupIterations.ListGroupIterations returned error: %v", err)
-	}
-
-	want := []*GroupIteration{{
-		ID:          53,
-		IID:         13,
-		Sequence:    1,
-		GroupID:     5,
-		Title:       "Iteration II",
-		Description: "Ipsum Lorem ipsum",
-		State:       2,
-		WebURL:      "http://gitlab.example.com/groups/my-group/-/iterations/13",
-	}}
-	if !reflect.DeepEqual(want, iterations) {
-		t.Errorf("GroupIterations.ListGroupIterations returned %+v, want %+v", iterations, want)
-	}
-}
diff --git a/group_labels_test.go b/group_labels_test.go
index f7f297a..8b75c56 100644
--- a/group_labels_test.go
+++ b/group_labels_test.go
@@ -30,11 +30,11 @@ func TestCreateGroupGroupLabel(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/groups/1/labels", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPost)
-		fmt.Fprint(w, `{"id":1, "name": "My / GroupLabel", "color" : "#11FF22"}`)
+		fmt.Fprint(w, `{"id":1, "name": "My GroupLabel", "color" : "#11FF22"}`)
 	})
 
 	l := &CreateGroupLabelOptions{
-		Name:  String("My / GroupLabel"),
+		Name:  String("My GroupLabel"),
 		Color: String("#11FF22"),
 	}
 	label, _, err := client.GroupLabels.CreateGroupLabel("1", l)
@@ -42,7 +42,7 @@ func TestCreateGroupGroupLabel(t *testing.T) {
 	if err != nil {
 		log.Fatal(err)
 	}
-	want := &GroupLabel{ID: 1, Name: "My / GroupLabel", Color: "#11FF22"}
+	want := &GroupLabel{ID: 1, Name: "My GroupLabel", Color: "#11FF22"}
 	if !reflect.DeepEqual(want, label) {
 		t.Errorf("GroupLabels.CreateGroupLabel returned %+v, want %+v", label, want)
 	}
@@ -57,7 +57,7 @@ func TestDeleteGroupLabel(t *testing.T) {
 	})
 
 	label := &DeleteGroupLabelOptions{
-		Name: String("My / GroupLabel"),
+		Name: String("My GroupLabel"),
 	}
 
 	_, err := client.GroupLabels.DeleteGroupLabel("1", label)
@@ -72,12 +72,12 @@ func TestUpdateGroupLabel(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/groups/1/labels", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		fmt.Fprint(w, `{"id":1, "name": "New / GroupLabel", "color" : "#11FF23" , "description":"This is updated label"}`)
+		fmt.Fprint(w, `{"id":1, "name": "New GroupLabel", "color" : "#11FF23" , "description":"This is updated label"}`)
 	})
 
 	l := &UpdateGroupLabelOptions{
-		Name:        String("My / GroupLabel"),
-		NewName:     String("New / GroupLabel"),
+		Name:        String("My GroupLabel"),
+		NewName:     String("New GroupLabel"),
 		Color:       String("#11FF23"),
 		Description: String("This is updated label"),
 	}
@@ -91,7 +91,7 @@ func TestUpdateGroupLabel(t *testing.T) {
 		log.Fatal(err)
 	}
 
-	want := &GroupLabel{ID: 1, Name: "New / GroupLabel", Color: "#11FF23", Description: "This is updated label"}
+	want := &GroupLabel{ID: 1, Name: "New GroupLabel", Color: "#11FF23", Description: "This is updated label"}
 
 	if !reflect.DeepEqual(want, label) {
 		t.Errorf("GroupLabels.UpdateGroupLabel returned %+v, want %+v", label, want)
@@ -104,14 +104,14 @@ func TestSubscribeToGroupLabel(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/groups/1/labels/5/subscribe", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPost)
-		fmt.Fprint(w, `{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
+		fmt.Fprint(w, `{  "id" : 5, "name" : "bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
 	})
 
 	label, _, err := client.GroupLabels.SubscribeToGroupLabel("1", "5")
 	if err != nil {
 		log.Fatal(err)
 	}
-	want := &GroupLabel{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
+	want := &GroupLabel{ID: 5, Name: "bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
 	if !reflect.DeepEqual(want, label) {
 		t.Errorf("GroupLabels.SubscribeToGroupLabel returned %+v, want %+v", label, want)
 	}
@@ -137,20 +137,18 @@ func TestListGroupLabels(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/groups/1/labels", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodGet)
-		fmt.Fprint(w, `[{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}]`)
+		fmt.Fprint(w, `[{  "id" : 5, "name" : "bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}]`)
 	})
 
 	o := &ListGroupLabelsOptions{
-		ListOptions: ListOptions{
-			Page:    1,
-			PerPage: 10,
-		},
+		Page:    1,
+		PerPage: 10,
 	}
 	label, _, err := client.GroupLabels.ListGroupLabels("1", o)
 	if err != nil {
 		t.Log(err.Error() == "invalid ID type 1.1, the ID must be an int or a string")
 	}
-	want := []*GroupLabel{{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}}
+	want := []*GroupLabel{{ID: 5, Name: "bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}}
 	if !reflect.DeepEqual(want, label) {
 		t.Errorf("GroupLabels.ListGroupLabels returned %+v, want %+v", label, want)
 	}
@@ -162,7 +160,7 @@ func TestGetGroupLabel(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/groups/1/labels/5", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodGet)
-		fmt.Fprint(w, `{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
+		fmt.Fprint(w, `{  "id" : 5, "name" : "bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
 	})
 
 	label, _, err := client.GroupLabels.GetGroupLabel("1", 5)
@@ -170,7 +168,7 @@ func TestGetGroupLabel(t *testing.T) {
 		t.Log(err)
 	}
 
-	want := &GroupLabel{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
+	want := &GroupLabel{ID: 5, Name: "bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
 	if !reflect.DeepEqual(want, label) {
 		t.Errorf("GroupLabels.GetGroupLabel returned %+v, want %+v", label, want)
 	}
diff --git a/labels_test.go b/labels_test.go
index eb262b3..bb9a2fc 100644
--- a/labels_test.go
+++ b/labels_test.go
@@ -107,14 +107,14 @@ func TestSubscribeToLabel(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/labels/5/subscribe", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPost)
-		fmt.Fprint(w, `{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
+		fmt.Fprint(w, `{  "id" : 5, "name" : "bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
 	})
 
 	label, _, err := client.Labels.SubscribeToLabel("1", "5")
 	if err != nil {
 		log.Fatal(err)
 	}
-	want := &Label{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
+	want := &Label{ID: 5, Name: "bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
 	if !reflect.DeepEqual(want, label) {
 		t.Errorf("Labels.SubscribeToLabel returned %+v, want %+v", label, want)
 	}
@@ -140,7 +140,7 @@ func TestListLabels(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/labels", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodGet)
-		fmt.Fprint(w, `[{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}]`)
+		fmt.Fprint(w, `[{  "id" : 5, "name" : "bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}]`)
 	})
 
 	o := &ListLabelsOptions{
@@ -153,7 +153,7 @@ func TestListLabels(t *testing.T) {
 	if err != nil {
 		t.Log(err.Error() == "invalid ID type 1.1, the ID must be an int or a string")
 	}
-	want := []*Label{{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}}
+	want := []*Label{{ID: 5, Name: "bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}}
 	if !reflect.DeepEqual(want, label) {
 		t.Errorf("Labels.ListLabels returned %+v, want %+v", label, want)
 	}
@@ -165,14 +165,14 @@ func TestGetLabel(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/labels/5", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodGet)
-		fmt.Fprint(w, `{  "id" : 5, "name" : "kind/bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
+		fmt.Fprint(w, `{  "id" : 5, "name" : "bug", "color" : "#d9534f", "description": "Bug reported by user", "open_issues_count": 1, "closed_issues_count": 0, "open_merge_requests_count": 1, "subscribed": true,"priority": null}`)
 	})
 
 	label, _, err := client.Labels.GetLabel("1", 5)
 	if err != nil {
 		t.Log(err)
 	}
-	want := &Label{ID: 5, Name: "kind/bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
+	want := &Label{ID: 5, Name: "bug", Color: "#d9534f", Description: "Bug reported by user", OpenIssuesCount: 1, ClosedIssuesCount: 0, OpenMergeRequestsCount: 1, Subscribed: true}
 	if !reflect.DeepEqual(want, label) {
 		t.Errorf("Labels.GetLabel returned %+v, want %+v", label, want)
 	}
diff --git a/merge_requests_test.go b/merge_requests_test.go
index 224e014..05f7281 100644
--- a/merge_requests_test.go
+++ b/merge_requests_test.go
@@ -144,7 +144,6 @@ func TestGetMergeRequest(t *testing.T) {
 	require.Equal(t, mergeRequest.UpdatedAt, &mrUpdate)
 	require.Equal(t, mergeRequest.FirstContribution, true)
 	require.Equal(t, mergeRequest.HasConflicts, true)
-	require.Equal(t, mergeRequest.Draft, true)
 }
 
 func TestListProjectMergeRequests(t *testing.T) {
diff --git a/packages_test.go b/packages_test.go
index cefef64..207b765 100644
--- a/packages_test.go
+++ b/packages_test.go
@@ -41,7 +41,6 @@ func TestPackagesService_ListProjectPackages(t *testing.T) {
 			WebPath:       "/foo/bar/-/packages/3",
 			DeleteAPIPath: "https://gitlab.example.com/api/v4/projects/1/packages/3",
 		},
-		Tags: []string{},
 	}}
 
 	ps, resp, err := client.Packages.ListProjectPackages(3, nil)
diff --git a/project_access_tokens_test.go b/project_access_tokens_test.go
index 6630fef..f960b32 100644
--- a/project_access_tokens_test.go
+++ b/project_access_tokens_test.go
@@ -120,6 +120,6 @@ func TestDeleteProjectAccessToken(t *testing.T) {
 
 	_, err := client.ProjectAccessTokens.DeleteProjectAccessToken("1", 1234)
 	if err != nil {
-		t.Errorf("ProjectAccessTokens.DeleteProjectAccessToken returned error: %v", err)
+		t.Errorf("Pipelines.DeleteProjectAccessToken returned error: %v", err)
 	}
 }
diff --git a/project_badges_test.go b/project_badges_test.go
index 41a41a0..284caf3 100644
--- a/project_badges_test.go
+++ b/project_badges_test.go
@@ -31,7 +31,6 @@ func TestProjectBadgesService_ListProjectBadges(t *testing.T) {
 
 	want := []*ProjectBadge{{
 		ID:               1,
-		Name:             "Coverage",
 		LinkURL:          "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}",
 		ImageURL:         "https://shields.io/my/badge",
 		RenderedLinkURL:  "http://example.com/ci_status.svg?project=example-org/example-project&ref=master",
@@ -81,7 +80,6 @@ func TestProjectBadgesService_GetProjectBadge(t *testing.T) {
 
 	want := &ProjectBadge{
 		ID:               1,
-		Name:             "Coverage",
 		LinkURL:          "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}",
 		ImageURL:         "https://shields.io/my/badge",
 		RenderedLinkURL:  "http://example.com/ci_status.svg?project=example-org/example-project&ref=master",
@@ -131,7 +129,6 @@ func TestProjectBadgesService_AddProjectBadge(t *testing.T) {
 
 	want := &ProjectBadge{
 		ID:               1,
-		Name:             "mybadge",
 		LinkURL:          "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}",
 		ImageURL:         "https://shields.io/my/badge",
 		RenderedLinkURL:  "http://example.com/ci_status.svg?project=example-org/example-project&ref=master",
@@ -181,7 +178,6 @@ func TestProjectBadgesService_EditProjectBadge(t *testing.T) {
 
 	want := &ProjectBadge{
 		ID:               1,
-		Name:             "mybadge",
 		LinkURL:          "http://example.com/ci_status.svg?project={project_path}&ref={default_branch}",
 		ImageURL:         "https://shields.io/my/badge",
 		RenderedLinkURL:  "http://example.com/ci_status.svg?project=example-org/example-project&ref=master",
diff --git a/project_iterations_test.go b/project_iterations_test.go
deleted file mode 100644
index ca304c6..0000000
--- a/project_iterations_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package gitlab
-
-import (
-	"fmt"
-	"net/http"
-	"reflect"
-	"testing"
-)
-
-func TestListProjectIterations(t *testing.T) {
-	mux, server, client := setup(t)
-	defer teardown(server)
-
-	mux.HandleFunc("/api/v4/projects/42/iterations",
-		func(w http.ResponseWriter, r *http.Request) {
-			testMethod(t, r, http.MethodGet)
-			fmt.Fprintf(w, `[
-        {
-          "id": 53,
-          "iid": 13,
-          "sequence": 1,
-          "group_id": 5,
-          "title": "Iteration II",
-          "description": "Ipsum Lorem ipsum",
-          "state": 2,
-          "web_url": "http://gitlab.example.com/groups/my-group/-/iterations/13"
-        }
-      ]`)
-		})
-
-	iterations, _, err := client.ProjectIterations.ListProjectIterations(42, &ListProjectIterationsOptions{})
-	if err != nil {
-		t.Errorf("GroupIterations.ListGroupIterations returned error: %v", err)
-	}
-
-	want := []*ProjectIteration{{
-		ID:          53,
-		IID:         13,
-		Sequence:    1,
-		GroupID:     5,
-		Title:       "Iteration II",
-		Description: "Ipsum Lorem ipsum",
-		State:       2,
-		WebURL:      "http://gitlab.example.com/groups/my-group/-/iterations/13",
-	}}
-	if !reflect.DeepEqual(want, iterations) {
-		t.Errorf("ProjectIterations.ListProjectIterations returned %+v, want %+v", iterations, want)
-	}
-}
diff --git a/project_snippets_test.go b/project_snippets_test.go
index cc54edc..bc60d3c 100644
--- a/project_snippets_test.go
+++ b/project_snippets_test.go
@@ -172,13 +172,7 @@ func TestProjectSnippetsService_CreateSnippet(t *testing.T) {
 			  },
 			  "project_id": 1,
 			  "web_url": "http://example.com/example/example/snippets/1",
-			  "raw_url": "http://example.com/example/example/snippets/1/raw",
-			  "files": [
-				{
-					"path": "add.rb",
-					"raw_url": "http://example.com/example/example/-/snippets/1/raw/main/add.rb"
-				}
-	   		  ]
+			  "raw_url": "http://example.com/example/example/snippets/1/raw"
 			}
 		`)
 	})
@@ -204,15 +198,6 @@ func TestProjectSnippetsService_CreateSnippet(t *testing.T) {
 		},
 		WebURL: "http://example.com/example/example/snippets/1",
 		RawURL: "http://example.com/example/example/snippets/1/raw",
-		Files: []struct {
-			Path   string `json:"path"`
-			RawURL string `json:"raw_url"`
-		}{
-			{
-				Path:   "add.rb",
-				RawURL: "http://example.com/example/example/-/snippets/1/raw/main/add.rb",
-			},
-		},
 	}
 
 	s, resp, err := client.ProjectSnippets.CreateSnippet(1, nil, nil, nil)
diff --git a/project_variables_test.go b/project_variables_test.go
index 235f3a6..de1de74 100644
--- a/project_variables_test.go
+++ b/project_variables_test.go
@@ -61,7 +61,6 @@ func TestProjectVariablesService_GetVariable(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/variables/TEST_VARIABLE_1", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodGet)
-		testParams(t, r, "filter%5Benvironment_scope%5D=prod")
 		fmt.Fprintf(w, `
 			{
 				"key": "TEST_VARIABLE_1",
@@ -82,7 +81,7 @@ func TestProjectVariablesService_GetVariable(t *testing.T) {
 		EnvironmentScope: "",
 	}
 
-	pv, resp, err := client.ProjectVariables.GetVariable(1, "TEST_VARIABLE_1", &GetProjectVariableOptions{Filter: &VariableFilter{EnvironmentScope: "prod"}}, nil)
+	pv, resp, err := client.ProjectVariables.GetVariable(1, "TEST_VARIABLE_1", nil, nil)
 	require.NoError(t, err)
 	require.NotNil(t, resp)
 	require.Equal(t, want, pv)
@@ -157,7 +156,6 @@ func TestProjectVariablesService_UpdateVariable(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/variables/NEW_VARIABLE", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPut)
-		testBody(t, r, `{"filter":{"environment_scope":"prod"}}`)
 		fmt.Fprintf(w, `
 			{
 				"key": "NEW_VARIABLE",
@@ -179,7 +177,7 @@ func TestProjectVariablesService_UpdateVariable(t *testing.T) {
 		EnvironmentScope: "*",
 	}
 
-	pv, resp, err := client.ProjectVariables.UpdateVariable(1, "NEW_VARIABLE", &UpdateProjectVariableOptions{Filter: &VariableFilter{EnvironmentScope: "prod"}}, nil)
+	pv, resp, err := client.ProjectVariables.UpdateVariable(1, "NEW_VARIABLE", nil, nil)
 	require.NoError(t, err)
 	require.NotNil(t, resp)
 	require.Equal(t, want, pv)
@@ -206,10 +204,9 @@ func TestProjectVariablesService_RemoveVariable(t *testing.T) {
 
 	mux.HandleFunc("/api/v4/projects/1/variables/VARIABLE_1", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodDelete)
-		testParams(t, r, "filter%5Benvironment_scope%5D=prod")
 	})
 
-	resp, err := client.ProjectVariables.RemoveVariable(1, "VARIABLE_1", &RemoveProjectVariableOptions{Filter: &VariableFilter{EnvironmentScope: "prod"}}, nil)
+	resp, err := client.ProjectVariables.RemoveVariable(1, "VARIABLE_1", nil, nil)
 	require.NoError(t, err)
 	require.NotNil(t, resp)
 
diff --git a/projects_test.go b/projects_test.go
index 90815e7..b7becaf 100644
--- a/projects_test.go
+++ b/projects_test.go
@@ -751,20 +751,20 @@ func TestForkProject(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)
 
-	namespaceID := 42
+	namespace := "mynamespace"
 	name := "myreponame"
 	path := "myrepopath"
 
 	mux.HandleFunc("/api/v4/projects/1/fork", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPost)
-		testBody(t, r, fmt.Sprintf(`{"name":"%s","namespace_id":%d,"path":"%s"}`, name, namespaceID, path))
+		testBody(t, r, fmt.Sprintf(`{"name":"%s","namespace":"%s","path":"%s"}`, name, namespace, path))
 		fmt.Fprint(w, `{"id":2}`)
 	})
 
 	project, _, err := client.Projects.ForkProject(1, &ForkProjectOptions{
-		NamespaceID: Int(namespaceID),
-		Name:        String(name),
-		Path:        String(path),
+		Namespace: String(namespace),
+		Name:      String(name),
+		Path:      String(path),
 	})
 	if err != nil {
 		t.Errorf("Projects.ForkProject returned error: %v", err)
diff --git a/testdata/create_group_access_token.json b/testdata/create_group_access_token.json
deleted file mode 100644
index d645538..0000000
--- a/testdata/create_group_access_token.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-  "id": 1876,
-  "name": "token 10",
-  "revoked": false,
-  "created_at": "2021-03-09T21:11:47.271Z",
-  "scopes": [
-      "api",
-      "read_api",
-      "read_repository",
-      "write_repository"
-  ],
-  "user_id": 2453,
-  "active": true,
-  "expires_at": null,
-  "token": "2UsevZE1x1ZdFZW4MNzH",
-  "access_level": 40
-}
diff --git a/testdata/get_merge_request.json b/testdata/get_merge_request.json
index 8825fd0..39f592e 100644
--- a/testdata/get_merge_request.json
+++ b/testdata/get_merge_request.json
@@ -160,6 +160,5 @@
   },
   "first_contribution": true,
   "approvals_before_merge": 1,
-  "has_conflicts": true,
-  "draft": true
+  "has_conflicts": true
 }
diff --git a/testdata/list_group_access_tokens.json b/testdata/list_group_access_tokens.json
deleted file mode 100644
index 99327ba..0000000
--- a/testdata/list_group_access_tokens.json
+++ /dev/null
@@ -1,34 +0,0 @@
-[
-  {
-      "id": 1876,
-      "name": "token 10",
-      "revoked": false,
-      "created_at": "2021-03-09T21:11:47.271Z",
-      "scopes": [
-          "api",
-          "read_api",
-          "read_repository",
-          "write_repository"
-      ],
-      "user_id": 2453,
-      "active": true,
-      "expires_at": null,
-      "access_level": 40
-  },
-  {
-      "id": 1877,
-      "name": "token 8",
-      "revoked": false,
-      "created_at": "2021-03-09T21:11:47.340Z",
-      "scopes": [
-          "api",
-          "read_api",
-          "read_repository",
-          "write_repository"
-      ],
-      "user_id": 2456,
-      "active": true,
-      "expires_at": null,
-      "access_level": 30
-  }
-]
diff --git a/testdata/list_group_iterations.json b/testdata/list_group_iterations.json
deleted file mode 100644
index 9d032c0..0000000
--- a/testdata/list_group_iterations.json
+++ /dev/null
@@ -1,16 +0,0 @@
-[
-    {
-      "id": 53,
-      "iid": 13,
-      "sequence": 1,
-      "group_id": 5,
-      "title": "Iteration II",
-      "description": "Ipsum Lorem ipsum",
-      "state": 2,
-      "created_at": "2020-01-27T05:07:12.573Z",
-      "updated_at": "2020-01-27T05:07:12.573Z",
-      "due_date": "2020-02-01",
-      "start_date": "2020-02-14",
-      "web_url": "http://gitlab.example.com/groups/my-group/-/iterations/13"
-    }
-]
diff --git a/testdata/webhooks/subgroup.json b/testdata/webhooks/subgroup.json
new file mode 100644
index 0000000..0c336cd
--- /dev/null
+++ b/testdata/webhooks/subgroup.json
@@ -0,0 +1,13 @@
+{
+  "created_at": "2022-01-24T14:23:59Z",
+  "updated_at": "2022-01-24T14:23:59Z",
+  "event_name": "subgroup_create",
+  "name": "Subgroup 1",
+  "path": "subgroup-1",
+  "full_path": "group-1/subgroup-1",
+  "group_id": 2,
+  "parent_group_id": 1,
+  "parent_name": "Group 1",
+  "parent_path": "group-1",
+  "parent_full_path": "group-1"
+}