Repository: xanzy/go-gitlab
Pull request #672 (2019-08-28 16:46:09)

Add Enterprise LDAP group links
Closes https://github.com/xanzy/go-gitlab/issues/605

benoitmenard-richemont: Hi,

When do you think this MR will be merged ?

Thanks a lot,
Benoit
svanharmelen: Well it looks like @zoidbergwill has abandoned the PR... I can maybe have a look at tweaking the PR myself, but sure when I'll have some time for that.

It will help if you can answer the last question about some docs or a raw API response that shows that the fields added in this PR are actually there...
benoitmenard-richemont: There is no docs about this on Gitlab.com, but we use Gitlab EE so I can get the JSON response if you want.

I will start a new PR if you want.
svanharmelen: We can use this PR (I can update it), so that's fine... Having a JSON response would  be great 👍 
zoidbergwill: I can update it today. Didn't see this in my email. It's been a long while since I opened it.
svanharmelen: Cool, that would be very nice @zoidbergwill. Thanks!
zoidbergwill: @benoitmenard-richemont I'm no longer on the team that manages GitLab at my company, so would you be able to test whether creating an LDAP Group Link returns the single link? vs a list? and what response deleting a link returns? 

Other than that I'm confident with my changes, going with assuming that they return the same things as other API endpoints of the GitLab API.

I guess I could check the GitLab codebase.

Then we can add tests for adding and deleting, and then I'm happy with addressing all the feedback.
jasoncarlsen: @zoidbergwill I'm an admin of my company's GitLab instance so I can help answer the API response questions.  When deleting an LDAP group link there is no content in the response (204).  The response when creating an LDAP group link is:

```
{
  'cn': 'group_that_was_added',
  'group_access': 40,
  'provider': 'provider_for_group_that_was_added'
}
```

Patch:
diff --git a/groups.go b/groups.go
index 7925678..0daf75e 100644
--- a/groups.go
+++ b/groups.go
@@ -60,11 +60,18 @@ type Group struct {
 	SharedProjects                 []*Project                 `json:"shared_projects"`
 	LDAPCN                         string                     `json:"ldap_cn"`
 	LDAPAccess                     AccessLevelValue           `json:"ldap_access"`
+	LDAPGroupLinks                 []*LDAPGroupLink           `json:"ldap_group_links"`
 	SharedRunnersMinutesLimit      int                        `json:"shared_runners_minutes_limit"`
 	ExtraSharedRunnersMinutesLimit int                        `json:"extra_shared_runners_minutes_limit"`
 	PendingDelete                  bool                       `json:"pending_delete"`
 }
 
+type LDAPGroupLink struct {
+	CN          string           `json:"cn"`
+	GroupAccess AccessLevelValue `json:"group_access"`
+	Provider    string           `json:"provider"`
+}
+
 // ListGroupsOptions represents the available ListGroups() options.
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/groups.html#list-project-groups
@@ -347,3 +354,110 @@ func (s *GroupsService) ListSubgroups(gid interface{}, opt *ListSubgroupsOptions
 
 	return g, resp, err
 }
+
+// ListGroupLDAPLinks lists the group's LDAP links. Available only for users who
+// can edit groups.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/groups.html#list-ldap-group-links-starter
+func (s *GroupsService) ListGroupLDAPLinks(gid interface{}, options ...OptionFunc) ([]*LDAPGroupLink, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/ldap_group_links", pathEscape(group))
+
+	req, err := s.client.NewRequest("GET", u, nil, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var gl []*LDAPGroupLink
+	resp, err := s.client.Do(req, &gl)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gl, resp, nil
+}
+
+// AddGroupLDAPLinkOptions represents the available AddGroupLDAPLink() options.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/groups.html#add-ldap-group-link-starter
+type AddGroupLDAPLinkOptions struct {
+	CN          *string `url:"cn,omitempty" json:"cn,omitempty"`
+	GroupAccess *int    `url:"group_access,omitempty" json:"group_access,omitempty"`
+	Provider    *string `url:"provider,omitempty" json:"provider,ommitempty"`
+}
+
+// AddGroupLDAPLink creates a new group LDAP link. Available only for users who
+// can edit groups.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/groups.html#add-ldap-group-link-starter
+func (s *GroupsService) AddGroupLDAPLink(gid interface{}, opt *AddGroupLDAPLinkOptions, options ...OptionFunc) (*LDAPGroupLink, *Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, nil, err
+	}
+	u := fmt.Sprintf("groups/%s/ldap_group_links", pathEscape(group))
+
+	req, err := s.client.NewRequest("POST", u, opt, options)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	gl := new(LDAPGroupLink)
+	resp, err := s.client.Do(req, gl)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return gl, resp, err
+}
+
+// DeleteGroupLDAPLink deletes a group LDAP link. Available only for users who
+// can edit groups.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link-starter
+func (s *GroupsService) DeleteGroupLDAPLink(gid interface{}, cn string, options ...OptionFunc) (*Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf("groups/%s/ldap_group_links/%s", pathEscape(group), pathEscape(cn))
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}
+
+// DeleteGroupLDAPLinkForProvider deletes a group LDAP link from a specific
+// provider. Available only for users who can edit groups.
+//
+// GitLab API docs:
+// https://docs.gitlab.com/ee/api/groups.html#delete-ldap-group-link-starter
+func (s *GroupsService) DeleteGroupLDAPLinkForProvider(gid interface{}, provider, cn string, options ...OptionFunc) (*Response, error) {
+	group, err := parseID(gid)
+	if err != nil {
+		return nil, err
+	}
+	u := fmt.Sprintf(
+		"groups/%s/ldap_group_links/%s/%s",
+		pathEscape(group),
+		pathEscape(provider),
+		pathEscape(cn),
+	)
+
+	req, err := s.client.NewRequest("DELETE", u, nil, options)
+	if err != nil {
+		return nil, err
+	}
+
+	return s.client.Do(req, nil)
+}


Test patch:
diff --git a/groups_test.go b/groups_test.go
index 8c8a180..3744d6d 100644
--- a/groups_test.go
+++ b/groups_test.go
@@ -203,3 +203,82 @@ func TestListSubgroups(t *testing.T) {
 		t.Errorf("Groups.ListSubgroups returned %+v, want %+v", groups, want)
 	}
 }
+
+func TestListGroupLDAPLinks(t *testing.T) {
+	mux, server, client := setup()
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/groups/1/ldap_group_links",
+		func(w http.ResponseWriter, r *http.Request) {
+			testMethod(t, r, "GET")
+			fmt.Fprint(w, `[
+	{
+		"cn":"gitlab_group_example_30",
+		"group_access":30,
+		"provider":"example_ldap_provider"
+	},
+	{
+		"cn":"gitlab_group_example_40",
+		"group_access":40,
+		"provider":"example_ldap_provider"
+	}
+]`)
+		})
+
+	links, _, err := client.Groups.ListGroupLDAPLinks(1)
+	if err != nil {
+		t.Errorf("Groups.ListGroupLDAPLinks returned error: %v", err)
+	}
+
+	want := []*LDAPGroupLink{
+		{
+			CN:          "gitlab_group_example_30",
+			GroupAccess: 30,
+			Provider:    "example_ldap_provider",
+		},
+		{
+			CN:          "gitlab_group_example_40",
+			GroupAccess: 40,
+			Provider:    "example_ldap_provider",
+		},
+	}
+	if !reflect.DeepEqual(want, links) {
+		t.Errorf("Groups.ListGroupLDAPLinks returned %+v, want %+v", links, want)
+	}
+}
+
+func TestAddGroupLDAPLink(t *testing.T) {
+	mux, server, client := setup()
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/groups/1/ldap_group_links",
+		func(w http.ResponseWriter, r *http.Request) {
+			testMethod(t, r, "POST")
+			fmt.Fprint(w, `
+{
+	"cn":"gitlab_group_example_30",
+	"group_access":30,
+	"provider":"example_ldap_provider"
+}`)
+		})
+
+	opt := &AddGroupLDAPLinkOptions{
+		CN:          String("gitlab_group_example_30"),
+		GroupAccess: Int(30),
+		Provider:    String("example_ldap_provider"),
+	}
+
+	link, _, err := client.Groups.AddGroupLDAPLink(1, opt)
+	if err != nil {
+		t.Errorf("Groups.AddGroupLDAPLink returned error: %v", err)
+	}
+
+	want := &LDAPGroupLink{
+		CN:          "gitlab_group_example_30",
+		GroupAccess: 30,
+		Provider:    "example_ldap_provider",
+	}
+	if !reflect.DeepEqual(want, link) {
+		t.Errorf("Groups.AddGroupLDAPLink returned %+v, want %+v", link, want)
+	}
+}
diff --git a/protected_branches_test.go b/protected_branches_test.go
index ba2a545..3065aa4 100644
--- a/protected_branches_test.go
+++ b/protected_branches_test.go
@@ -132,6 +132,7 @@ func TestProtectRepositoryBranches(t *testing.T) {
 		t.Errorf("ProtectedBranches.ProtectRepositoryBranches returned error: %v", err)
 	}
 	want := &ProtectedBranch{
+		ID:   1,
 		Name: "master",
 		PushAccessLevels: []*BranchAccessDescription{
 			{