Repository: xanzy/go-gitlab
Pull request #1527 (2022-08-16 16:16:32)

Add Group SAML Links
Gitlab Group SAML Group links added to production API yesterday: 
https://docs.gitlab.com/ee/api/groups.html#saml-group-links


Patch:
diff --git a/groups.go b/groups.go
index cfd9ef5..3535b83 100644
--- a/groups.go
+++ b/groups.go
@@ -75,6 +75,7 @@ type Group struct {
 	LDAPCN                         string           `json:"ldap_cn"`
 	LDAPAccess                     AccessLevelValue `json:"ldap_access"`
 	LDAPGroupLinks                 []*LDAPGroupLink `json:"ldap_group_links"`
+	SAMLGroupLinks                 []*SAMLGroupLink `json:"saml_group_links"`
 	SharedRunnersMinutesLimit      int              `json:"shared_runners_minutes_limit"`
 	ExtraSharedRunnersMinutesLimit int              `json:"extra_shared_runners_minutes_limit"`
 	PreventForkingOutsideGroup     bool             `json:"prevent_forking_outside_group"`
@@ -100,6 +101,14 @@ type LDAPGroupLink struct {
 	Provider    string           `json:"provider"`
 }
 
+// SAMLGroupLink represents a GitLab SAML group link.
+//
+// GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#saml-group-links
+type SAMLGroupLink struct {
+	AccessLevel string `json:"access_level"`
+	Name        string `json:"name"`
+}
+
 // ListGroupsOptions represents the available ListGroups() options.
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-project-groups
@@ -744,6 +753,113 @@ func (s *GroupsService) DeleteGroupLDAPLinkForProvider(gid interface{}, provider
 	return s.client.Do(req, nil)
 }
 
+// ListGroupSAMLLinks lists the group's SAML links. Available only for users who
+// can edit groups.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/groups.html#list-saml-group-links
+func (s *GroupsService) ListGroupSAMLLinks(gid interface{}, options ...RequestOptionFunc) ([]*SAMLGroupLink, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/saml_group_links", PathEscape(group))
+
+	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var gls []*SAMLGroupLink
+	resp, err := s.client.Do(req, &gls)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gls, resp, nil
+}
+
+// GetGroupSAMLLink get a specific group SAML link. Available only for users who
+// can edit groups.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/groups.html#get-saml-group-link
+func (s *GroupsService) GetGroupSAMLLink(gid interface{}, saml_group_name string, options ...RequestOptionFunc) (*SAMLGroupLink, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/saml_group_links/%s", PathEscape(group), PathEscape(saml_group_name))
+
+	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	gl := new(SAMLGroupLink)
+	resp, err := s.client.Do(req, &gl)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gl, resp, nil
+}
+
+// AddGroupSAMLLinkOptions represents the available AddGroupSAMLLink() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/groups.html#add-saml-group-link
+type AddGroupSAMLLinkOptions struct {
+	AccessLevel   *string `url:"access_level,omitempty" json:"access_level,omitempty"`
+	SamlGroupName *string `url:"saml_group_name,omitempty" json:"saml_group_name,omitempty"`
+}
+
+// AddGroupSAMLLink creates a new group SAML link. Available only for users who
+// can edit groups.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/groups.html#add-saml-group-link
+func (s *GroupsService) AddGroupSAMLLink(gid interface{}, opt *AddGroupSAMLLinkOptions, options ...RequestOptionFunc) (*SAMLGroupLink, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/saml_group_links", PathEscape(group))
+
+	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	gl := new(SAMLGroupLink)
+	resp, err := s.client.Do(req, &gl)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gl, resp, err
+}
+
+// DeleteGroupSAMLLink deletes a group SAML link. Available only for users who
+// can edit groups.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/groups.html#delete-saml-group-link
+func (s *GroupsService) DeleteGroupSAMLLink(gid interface{}, saml_group_name string, options ...RequestOptionFunc) (*Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("groups/%s/saml_group_links/%s", PathEscape(group), PathEscape(saml_group_name))
+
+	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
 // ShareGroupWithGroupOptions represents the available ShareGroupWithGroup() options.
 //
 // GitLab API docs:


Test patch:
diff --git a/groups_test.go b/groups_test.go
index 8a37a43..1a01580 100644
--- a/groups_test.go
+++ b/groups_test.go
@@ -364,6 +364,106 @@ func TestAddGroupLDAPLinkFilter(t *testing.T) {
 	}
 }
 
+func TestListGroupSAMLLinks(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/groups/1/saml_group_links",
+		func(w http.ResponseWriter, r *http.Request) {
+			testMethod(t, r, http.MethodGet)
+			fmt.Fprint(w, `[
+	{
+		"access_level":"Developer",
+		"name":"gitlab_group_example_developer"
+	},
+	{
+		"access_level":"Maintainer",
+		"name":"gitlab_group_example_maintainer"
+	}
+]`)
+		})
+
+	links, _, err := client.Groups.ListGroupSAMLLinks(1)
+	if err != nil {
+		t.Errorf("Groups.ListGroupSAMLLinks returned error: %v", err)
+	}
+
+	want := []*SAMLGroupLink{
+		{
+			AccessLevel: "Developer",
+			Name:        "gitlab_group_example_developer",
+		},
+		{
+			AccessLevel: "Maintainer",
+			Name:        "gitlab_group_example_maintainer",
+		},
+	}
+	if !reflect.DeepEqual(want, links) {
+		t.Errorf("Groups.ListGroupSAMLLinks returned %+v, want %+v", links, want)
+	}
+}
+
+func TestGetGroupSAMLLink(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/groups/1/saml_group_links/gitlab_group_example_developer",
+		func(w http.ResponseWriter, r *http.Request) {
+			testMethod(t, r, http.MethodGet)
+			fmt.Fprint(w, `
+{
+	"access_level":"Developer",
+	"name":"gitlab_group_example_developer"
+}`)
+		})
+
+	links, _, err := client.Groups.GetGroupSAMLLink(1, "gitlab_group_example_developer")
+	if err != nil {
+		t.Errorf("Groups.GetGroupSAMLLinks returned error: %v", err)
+	}
+
+	want := &SAMLGroupLink{
+		AccessLevel: "Developer",
+		Name:        "gitlab_group_example_developer",
+	}
+	if !reflect.DeepEqual(want, links) {
+		t.Errorf("Groups.GetGroupSAMLLink returned %+v, want %+v", links, want)
+	}
+}
+
+func TestAddGroupSAMLLink(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/groups/1/saml_group_links",
+		func(w http.ResponseWriter, r *http.Request) {
+			testMethod(t, r, http.MethodPost)
+			fmt.Fprint(w, `
+{
+	"access_level":"Developer",
+	"name":"gitlab_group_example_developer"
+}`)
+		})
+
+	opt := &AddGroupSAMLLinkOptions{
+		AccessLevel:   String("Developer"),
+		SamlGroupName: String("gitlab_group_example_developer"),
+	}
+
+	link, _, err := client.Groups.AddGroupSAMLLink(1, opt)
+	if err != nil {
+		t.Errorf("Groups.AddGroupSAMLLink returned error: %v", err)
+	}
+
+	want := &SAMLGroupLink{
+		AccessLevel: "Developer",
+		Name:        "gitlab_group_example_developer",
+	}
+	if !reflect.DeepEqual(want, link) {
+		t.Errorf("Groups.AddGroupSAMLLink returned %+v, want %+v", link, want)
+	}
+}
+
 func TestRestoreGroup(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)