Repository: google/go-github
Pull request #1214 (2019-07-02 12:20:08)

Add methods for GHE endpoints
Hello,
This PR adds methods for Github Enterprise endpoints.

The following are covered:
- Create organization
- Create User
- Delete User

I will be happy to add a few fix on this PR if necessary.

googlebot: We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors.  If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA ([login here](https://cla.developers.google.com/) to double check)?  If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and have the pull request author add another comment and the bot will run again. If the bot doesn't comment, it means it doesn't think anything has changed.

ℹ️ **Googlers: [Go here](https://goto.google.com/prinfo/https%3A%2F%2Fgithub.com%2Fgoogle%2Fgo-github%2Fpull%2F1214) for more info**.

<!-- need_author_cla -->
codecov[bot]: # [Codecov](https://codecov.io/gh/google/go-github/pull/1214?src=pr&el=h1) Report
> Merging [#1214](https://codecov.io/gh/google/go-github/pull/1214?src=pr&el=desc) into [master](https://codecov.io/gh/google/go-github/commit/0ffc27d42b7ad05ff6890030162d9a3d495224e8?src=pr&el=desc) will **decrease** coverage by `0.04%`.
> The diff coverage is `65.71%`.

[![Impacted file tree graph](https://codecov.io/gh/google/go-github/pull/1214/graphs/tree.svg?width=650&token=EgYjGjZlI0&height=150&src=pr)](https://codecov.io/gh/google/go-github/pull/1214?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1214      +/-   ##
==========================================
- Coverage   73.39%   73.34%   -0.05%     
==========================================
  Files          84       86       +2     
  Lines        5949     5984      +35     
==========================================
+ Hits         4366     4389      +23     
- Misses        825      831       +6     
- Partials      758      764       +6
```


| [Impacted Files](https://codecov.io/gh/google/go-github/pull/1214?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [github/admin\_users.go](https://codecov.io/gh/google/go-github/pull/1214/diff?src=pr&el=tree#diff-Z2l0aHViL2FkbWluX3VzZXJzLmdv) | `63.63% <63.63%> (ø)` | |
| [github/admin\_orgs.go](https://codecov.io/gh/google/go-github/pull/1214/diff?src=pr&el=tree#diff-Z2l0aHViL2FkbWluX29yZ3MuZ28=) | `69.23% <69.23%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/google/go-github/pull/1214?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/google/go-github/pull/1214?src=pr&el=footer). Last update [0ffc27d...0fe99b4](https://codecov.io/gh/google/go-github/pull/1214?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

fiahil: CLA fixed.
googlebot: So there's good news and bad news.

:thumbsup: The good news is that everyone that needs to sign a CLA (the pull request submitter and all commit authors) have done so.  Everything is all good there.

:confused: The bad news is that it appears that one or more commits were authored or co-authored by someone other than the pull request submitter.  We need to confirm that all authors are ok with their commits being contributed to this project.  Please have them confirm that here in the pull request.

*Note to project maintainer: This is a terminal state, meaning the `cla/google` commit status will not change from this state. It's up to you to confirm consent of all the commit author(s), set the `cla` label to `yes` (if enabled on your project), and then merge this pull request when appropriate.*

ℹ️ **Googlers: [Go here](https://goto.google.com/prinfo/https%3A%2F%2Fgithub.com%2Fgoogle%2Fgo-github%2Fpull%2F1214) for more info**.

<!-- need_author_consent -->
Misteur-Z: Ok for my commits being contributed to this project :+1: 

Patch:
diff --git a/AUTHORS b/AUTHORS
index b1faeb0f..8a41ba02 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -70,6 +70,7 @@ Dave Du Cros <davidducros@gmail.com>
 Dave Henderson <dhenderson@gmail.com>
 David Deng <daviddengcn@gmail.com>
 David Jannotta <djannotta@gmail.com>
+David Ji <github.com/davidji99>
 Davide Zipeto <dawez1@gmail.com>
 Dennis Webb <dennis@bluesentryit.com>
 Dhi Aurrahman <diorahman@rockybars.com>
diff --git a/github/admin_orgs.go b/github/admin_orgs.go
new file mode 100644
index 00000000..f0638310
--- /dev/null
+++ b/github/admin_orgs.go
@@ -0,0 +1,43 @@
+// Copyright 2019 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import "context"
+
+// createOrgRequest is a subset of Organization and is used internally
+// by CreateOrg to pass only the known fields for the endpoint.
+type createOrgRequest struct {
+	Login *string `json:"login,omitempty"`
+	Admin *string `json:"admin,omitempty"`
+}
+
+// CreateOrg creates a new organization in GitHub Enterprise.
+//
+// Note that only a subset of the org fields are used and org must
+// not be nil.
+//
+// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/orgs/#create-an-organization
+func (s *AdminService) CreateOrg(ctx context.Context, org *Organization, admin string) (*Organization, *Response, error) {
+	u := "admin/organizations"
+
+	orgReq := &createOrgRequest{
+		Login: org.Login,
+		Admin: &admin,
+	}
+
+	req, err := s.client.NewRequest("POST", u, orgReq)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	o := new(Organization)
+	resp, err := s.client.Do(ctx, req, o)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return o, resp, nil
+}
diff --git a/github/admin_stats.go b/github/admin_stats.go
index b5645f8c..dabefde3 100644
--- a/github/admin_stats.go
+++ b/github/admin_stats.go
@@ -10,7 +10,7 @@ import (
 	"fmt"
 )
 
-// AdminStats represents a variety of stats of a Github Enterprise
+// AdminStats represents a variety of stats of a GitHub Enterprise
 // installation.
 type AdminStats struct {
 	Issues     *IssueStats     `json:"issues,omitempty"`
@@ -147,7 +147,7 @@ func (s RepoStats) String() string {
 	return Stringify(s)
 }
 
-// GetAdminStats returns a variety of metrics about a Github Enterprise
+// GetAdminStats returns a variety of metrics about a GitHub Enterprise
 // installation.
 //
 // Please note that this is only available to site administrators,
diff --git a/github/admin_users.go b/github/admin_users.go
new file mode 100644
index 00000000..ea7a47d3
--- /dev/null
+++ b/github/admin_users.go
@@ -0,0 +1,61 @@
+// Copyright 2019 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+	"context"
+)
+
+// createUserRequest is a subset of User and is used internally
+// by CreateUser to pass only the known fields for the endpoint.
+type createUserRequest struct {
+	Login *string `json:"login,omitempty"`
+	Email *string `json:"email,omitempty"`
+}
+
+// CreateUser creates a new user in GitHub Enterprise.
+//
+// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#create-a-new-user
+func (s *AdminService) CreateUser(ctx context.Context, login, email string) (*User, *Response, error) {
+	u := "admin/users"
+
+	userReq := &createUserRequest{
+		Login: &login,
+		Email: &email,
+	}
+
+	req, err := s.client.NewRequest("POST", u, userReq)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	var user User
+	resp, err := s.client.Do(ctx, req, &user)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return &user, resp, nil
+}
+
+// DeleteUser deletes a user in GitHub Enterprise.
+//
+// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#delete-a-user
+func (s *AdminService) DeleteUser(ctx context.Context, username string) (*Response, error) {
+	u := "admin/users/" + username
+
+	req, err := s.client.NewRequest("DELETE", u, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	resp, err := s.client.Do(ctx, req, nil)
+	if err != nil {
+		return resp, err
+	}
+
+	return resp, nil
+}
diff --git a/github/git_refs.go b/github/git_refs.go
index 3f381d5f..fe16880e 100644
--- a/github/git_refs.go
+++ b/github/git_refs.go
@@ -68,7 +68,10 @@ func (s *GitService) GetRef(ctx context.Context, owner string, repo string, ref
 	resp, err := s.client.Do(ctx, req, r)
 	if _, ok := err.(*json.UnmarshalTypeError); ok {
 		// Multiple refs, means there wasn't an exact match.
-		return nil, resp, errors.New("no exact match found for this ref")
+		return nil, resp, errors.New("multiple matches found for this ref")
+	} else if resp.StatusCode == 404 {
+		// No ref, there was no match for the ref
+		return nil, resp, errors.New("no match found for this ref")
 	} else if err != nil {
 		return nil, resp, err
 	}
diff --git a/github/github-accessors.go b/github/github-accessors.go
index b92e81fe..b4ca033d 100644
--- a/github/github-accessors.go
+++ b/github/github-accessors.go
@@ -3324,6 +3324,30 @@ func (h *HookStats) GetTotalHooks() int {
 	return *h.TotalHooks
 }
 
+// GetGroupDescription returns the GroupDescription field if it's non-nil, zero value otherwise.
+func (i *IDPGroup) GetGroupDescription() string {
+	if i == nil || i.GroupDescription == nil {
+		return ""
+	}
+	return *i.GroupDescription
+}
+
+// GetGroupID returns the GroupID field if it's non-nil, zero value otherwise.
+func (i *IDPGroup) GetGroupID() string {
+	if i == nil || i.GroupID == nil {
+		return ""
+	}
+	return *i.GroupID
+}
+
+// GetGroupName returns the GroupName field if it's non-nil, zero value otherwise.
+func (i *IDPGroup) GetGroupName() string {
+	if i == nil || i.GroupName == nil {
+		return ""
+	}
+	return *i.GroupName
+}
+
 // GetAuthorsCount returns the AuthorsCount field if it's non-nil, zero value otherwise.
 func (i *Import) GetAuthorsCount() int {
 	if i == nil || i.AuthorsCount == nil {
@@ -6132,6 +6156,14 @@ func (o *Organization) GetLogin() string {
 	return *o.Login
 }
 
+// GetMembersAllowedRepositoryCreationType returns the MembersAllowedRepositoryCreationType field if it's non-nil, zero value otherwise.
+func (o *Organization) GetMembersAllowedRepositoryCreationType() string {
+	if o == nil || o.MembersAllowedRepositoryCreationType == nil {
+		return ""
+	}
+	return *o.MembersAllowedRepositoryCreationType
+}
+
 // GetMembersCanCreateRepos returns the MembersCanCreateRepos field if it's non-nil, zero value otherwise.
 func (o *Organization) GetMembersCanCreateRepos() bool {
 	if o == nil || o.MembersCanCreateRepos == nil {
@@ -12388,6 +12420,14 @@ func (u *UserEmail) GetVerified() bool {
 	return *u.Verified
 }
 
+// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise.
+func (u *UserEmail) GetVisibility() string {
+	if u == nil || u.Visibility == nil {
+		return ""
+	}
+	return *u.Visibility
+}
+
 // GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise.
 func (u *UserLDAPMapping) GetAvatarURL() string {
 	if u == nil || u.AvatarURL == nil {
diff --git a/github/github.go b/github/github.go
index b1323f9e..7a82677c 100644
--- a/github/github.go
+++ b/github/github.go
@@ -147,6 +147,12 @@ const (
 
 	// https://developer.github.com/changes/2019-04-11-pulls-branches-for-commit/
 	mediaTypeListPullsOrBranchesForCommitPreview = "application/vnd.github.groot-preview+json"
+
+	// https://developer.github.com/changes/2019-06-12-team-sync/
+	mediaTypeTeamSyncPreview = "application/vnd.github.team-sync-preview+json"
+
+	// https://developer.github.com/v3/previews/#repository-creation-permissions
+	mediaTypeMemberAllowedRepoCreationTypePreview = "application/vnd.github.surtur-preview+json"
 )
 
 // A Client manages communication with the GitHub API.
diff --git a/github/orgs.go b/github/orgs.go
index c70039ba..e1aa20db 100644
--- a/github/orgs.go
+++ b/github/orgs.go
@@ -56,6 +56,10 @@ type Organization struct {
 	// MembersCanCreateRepos default value is true and is only used in Organizations.Edit.
 	MembersCanCreateRepos *bool `json:"members_can_create_repositories,omitempty"`
 
+	// MembersAllowedRepositoryCreationType denotes if organization members can create repositories
+	// and the type of repositories they can create. Possible values are: "all", "private", or "none".
+	MembersAllowedRepositoryCreationType *string `json:"members_allowed_repository_creation_type,omitempty"`
+
 	// API URLs
 	URL              *string `json:"url,omitempty"`
 	EventsURL        *string `json:"events_url,omitempty"`
@@ -160,6 +164,9 @@ func (s *OrganizationsService) Get(ctx context.Context, org string) (*Organizati
 		return nil, nil, err
 	}
 
+	// TODO: remove custom Accept header when this API fully launches.
+	req.Header.Set("Accept", mediaTypeMemberAllowedRepoCreationTypePreview)
+
 	organization := new(Organization)
 	resp, err := s.client.Do(ctx, req, organization)
 	if err != nil {
diff --git a/github/teams.go b/github/teams.go
index a8247a3b..b4953e63 100644
--- a/github/teams.go
+++ b/github/teams.go
@@ -476,3 +476,87 @@ func (s *TeamsService) RemoveTeamProject(ctx context.Context, teamID int64, proj
 
 	return s.client.Do(ctx, req, nil)
 }
+
+// IDPGroupList represents a list of external identity provider (IDP) groups.
+type IDPGroupList struct {
+	Groups []*IDPGroup `json:"groups,omitempty"`
+}
+
+// IDPGroup represents an external identity provider (IDP) group.
+type IDPGroup struct {
+	GroupID          *string `json:"group_id,omitempty"`
+	GroupName        *string `json:"group_name,omitempty"`
+	GroupDescription *string `json:"group_description,omitempty"`
+}
+
+// ListIDPGroupsInOrganization lists IDP groups available in an organization.
+//
+// GitHub API docs: https://developer.github.com/v3/teams/team_sync/#list-idp-groups-in-an-organization
+func (s *TeamsService) ListIDPGroupsInOrganization(ctx context.Context, org string, opt *ListOptions) (*IDPGroupList, *Response, error) {
+	u := fmt.Sprintf("orgs/%v/team-sync/groups", org)
+	u, err := addOptions(u, opt)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// TODO: remove custom Accept header when this API fully launches.
+	req.Header.Set("Accept", mediaTypeTeamSyncPreview)
+
+	groups := new(IDPGroupList)
+	resp, err := s.client.Do(ctx, req, groups)
+	if err != nil {
+		return nil, resp, err
+	}
+	return groups, resp, nil
+}
+
+// ListIDPGroupsForTeam lists IDP groups connected to a team on GitHub.
+//
+// GitHub API docs: https://developer.github.com/v3/teams/team_sync/#list-idp-groups-for-a-team
+func (s *TeamsService) ListIDPGroupsForTeam(ctx context.Context, teamID string) (*IDPGroupList, *Response, error) {
+	u := fmt.Sprintf("teams/%v/team-sync/group-mappings", teamID)
+
+	req, err := s.client.NewRequest("GET", u, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// TODO: remove custom Accept header when this API fully launches.
+	req.Header.Set("Accept", mediaTypeTeamSyncPreview)
+
+	groups := new(IDPGroupList)
+	resp, err := s.client.Do(ctx, req, groups)
+	if err != nil {
+		return nil, resp, err
+	}
+	return groups, resp, err
+}
+
+// CreateOrUpdateIDPGroupConnections creates, updates, or removes a connection between a team
+// and an IDP group.
+//
+// GitHub API docs: https://developer.github.com/v3/teams/team_sync/#create-or-update-idp-group-connections
+func (s *TeamsService) CreateOrUpdateIDPGroupConnections(ctx context.Context, teamID string, opt IDPGroupList) (*IDPGroupList, *Response, error) {
+	u := fmt.Sprintf("teams/%v/team-sync/group-mappings", teamID)
+
+	req, err := s.client.NewRequest("PATCH", u, opt)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// TODO: remove custom Accept header when this API fully launches.
+	req.Header.Set("Accept", mediaTypeTeamSyncPreview)
+
+	groups := new(IDPGroupList)
+	resp, err := s.client.Do(ctx, req, groups)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return groups, resp, nil
+}
diff --git a/github/users_emails.go b/github/users_emails.go
index 0bbd4627..78d21491 100644
--- a/github/users_emails.go
+++ b/github/users_emails.go
@@ -9,9 +9,10 @@ import "context"
 
 // UserEmail represents user's email address
 type UserEmail struct {
-	Email    *string `json:"email,omitempty"`
-	Primary  *bool   `json:"primary,omitempty"`
-	Verified *bool   `json:"verified,omitempty"`
+	Email      *string `json:"email,omitempty"`
+	Primary    *bool   `json:"primary,omitempty"`
+	Verified   *bool   `json:"verified,omitempty"`
+	Visibility *string `json:"visibility,omitempty"`
 }
 
 // ListEmails lists all email addresses for the authenticated user.


Test patch:
diff --git a/github/admin_orgs_test.go b/github/admin_orgs_test.go
new file mode 100644
index 00000000..6966a179
--- /dev/null
+++ b/github/admin_orgs_test.go
@@ -0,0 +1,47 @@
+// Copyright 2019 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+)
+
+func TestAdminOrgs_Create(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	input := &Organization{
+		Login: String("github"),
+	}
+
+	mux.HandleFunc("/admin/organizations", func(w http.ResponseWriter, r *http.Request) {
+		v := new(createOrgRequest)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "POST")
+		want := &createOrgRequest{Login: String("github"), Admin: String("ghAdmin")}
+		if !reflect.DeepEqual(v, want) {
+			t.Errorf("Request body = %+v, want %+v", v, want)
+		}
+
+		fmt.Fprint(w, `{"login":"github","id":1}`)
+	})
+
+	org, _, err := client.Admin.CreateOrg(context.Background(), input, "ghAdmin")
+	if err != nil {
+		t.Errorf("Admin.CreateOrg returned error: %v", err)
+	}
+
+	want := &Organization{ID: Int64(1), Login: String("github")}
+	if !reflect.DeepEqual(org, want) {
+		t.Errorf("Admin.CreateOrg returned %+v, want %+v", org, want)
+	}
+}
diff --git a/github/admin_users_test.go b/github/admin_users_test.go
new file mode 100644
index 00000000..ad5379e2
--- /dev/null
+++ b/github/admin_users_test.go
@@ -0,0 +1,57 @@
+// Copyright 2019 The go-github AUTHORS. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package github
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"reflect"
+	"testing"
+)
+
+func TestAdminUsers_Create(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/admin/users", func(w http.ResponseWriter, r *http.Request) {
+		v := new(createUserRequest)
+		json.NewDecoder(r.Body).Decode(v)
+
+		testMethod(t, r, "POST")
+		want := &createUserRequest{Login: String("github"), Email: String("email@domain.com")}
+		if !reflect.DeepEqual(v, want) {
+			t.Errorf("Request body = %+v, want %+v", v, want)
+		}
+
+		fmt.Fprint(w, `{"login":"github","id":1}`)
+	})
+
+	org, _, err := client.Admin.CreateUser(context.Background(), "github", "email@domain.com")
+	if err != nil {
+		t.Errorf("Admin.CreateUser returned error: %v", err)
+	}
+
+	want := &User{ID: Int64(1), Login: String("github")}
+	if !reflect.DeepEqual(org, want) {
+		t.Errorf("Admin.CreateUser returned %+v, want %+v", org, want)
+	}
+}
+
+func TestAdminUsers_Delete(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/admin/users/github", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "DELETE")
+	})
+
+	_, err := client.Admin.DeleteUser(context.Background(), "github")
+	if err != nil {
+		t.Errorf("Admin.DeleteUser returned error: %v", err)
+	}
+}
diff --git a/github/git_refs_test.go b/github/git_refs_test.go
index 3cae3c08..6c8a1d87 100644
--- a/github/git_refs_test.go
+++ b/github/git_refs_test.go
@@ -56,6 +56,23 @@ func TestGitService_GetRef_singleRef(t *testing.T) {
 	}
 }
 
+func TestGitService_GetRef_noRefs(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/repos/o/r/git/refs/heads/b", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		w.WriteHeader(http.StatusNotFound)
+		fmt.Fprint(w, "[]")
+	})
+
+	_, _, err := client.Git.GetRef(context.Background(), "o", "r", "refs/heads/b")
+	want := "no match found for this ref"
+	if err.Error() != want {
+		t.Errorf("Git.GetRef returned %+v, want %+v", err, want)
+	}
+}
+
 func TestGitService_GetRef_multipleRefs(t *testing.T) {
 	client, mux, _, teardown := setup()
 	defer teardown()
@@ -87,7 +104,7 @@ func TestGitService_GetRef_multipleRefs(t *testing.T) {
 	})
 
 	_, _, err := client.Git.GetRef(context.Background(), "o", "r", "refs/heads/b")
-	want := "no exact match found for this ref"
+	want := "multiple matches found for this ref"
 	if err.Error() != want {
 		t.Errorf("Git.GetRef returned %+v, want %+v", err, want)
 	}
diff --git a/github/github-stringify_test.go b/github/github-stringify_test.go
index 6934f237..f424531e 100644
--- a/github/github-stringify_test.go
+++ b/github/github-stringify_test.go
@@ -793,42 +793,43 @@ func TestOrgStats_String(t *testing.T) {
 
 func TestOrganization_String(t *testing.T) {
 	v := Organization{
-		Login:                       String(""),
-		ID:                          Int64(0),
-		NodeID:                      String(""),
-		AvatarURL:                   String(""),
-		HTMLURL:                     String(""),
-		Name:                        String(""),
-		Company:                     String(""),
-		Blog:                        String(""),
-		Location:                    String(""),
-		Email:                       String(""),
-		Description:                 String(""),
-		PublicRepos:                 Int(0),
-		PublicGists:                 Int(0),
-		Followers:                   Int(0),
-		Following:                   Int(0),
-		TotalPrivateRepos:           Int(0),
-		OwnedPrivateRepos:           Int(0),
-		PrivateGists:                Int(0),
-		DiskUsage:                   Int(0),
-		Collaborators:               Int(0),
-		BillingEmail:                String(""),
-		Type:                        String(""),
-		Plan:                        &Plan{},
-		TwoFactorRequirementEnabled: Bool(false),
-		DefaultRepoPermission:       String(""),
-		DefaultRepoSettings:         String(""),
-		MembersCanCreateRepos:       Bool(false),
-		URL:                         String(""),
-		EventsURL:                   String(""),
-		HooksURL:                    String(""),
-		IssuesURL:                   String(""),
-		MembersURL:                  String(""),
-		PublicMembersURL:            String(""),
-		ReposURL:                    String(""),
-	}
-	want := `github.Organization{Login:"", ID:0, NodeID:"", AvatarURL:"", HTMLURL:"", Name:"", Company:"", Blog:"", Location:"", Email:"", Description:"", PublicRepos:0, PublicGists:0, Followers:0, Following:0, TotalPrivateRepos:0, OwnedPrivateRepos:0, PrivateGists:0, DiskUsage:0, Collaborators:0, BillingEmail:"", Type:"", Plan:github.Plan{}, TwoFactorRequirementEnabled:false, DefaultRepoPermission:"", DefaultRepoSettings:"", MembersCanCreateRepos:false, URL:"", EventsURL:"", HooksURL:"", IssuesURL:"", MembersURL:"", PublicMembersURL:"", ReposURL:""}`
+		Login:                                String(""),
+		ID:                                   Int64(0),
+		NodeID:                               String(""),
+		AvatarURL:                            String(""),
+		HTMLURL:                              String(""),
+		Name:                                 String(""),
+		Company:                              String(""),
+		Blog:                                 String(""),
+		Location:                             String(""),
+		Email:                                String(""),
+		Description:                          String(""),
+		PublicRepos:                          Int(0),
+		PublicGists:                          Int(0),
+		Followers:                            Int(0),
+		Following:                            Int(0),
+		TotalPrivateRepos:                    Int(0),
+		OwnedPrivateRepos:                    Int(0),
+		PrivateGists:                         Int(0),
+		DiskUsage:                            Int(0),
+		Collaborators:                        Int(0),
+		BillingEmail:                         String(""),
+		Type:                                 String(""),
+		Plan:                                 &Plan{},
+		TwoFactorRequirementEnabled:          Bool(false),
+		DefaultRepoPermission:                String(""),
+		DefaultRepoSettings:                  String(""),
+		MembersCanCreateRepos:                Bool(false),
+		MembersAllowedRepositoryCreationType: String(""),
+		URL:                                  String(""),
+		EventsURL:                            String(""),
+		HooksURL:                             String(""),
+		IssuesURL:                            String(""),
+		MembersURL:                           String(""),
+		PublicMembersURL:                     String(""),
+		ReposURL:                             String(""),
+	}
+	want := `github.Organization{Login:"", ID:0, NodeID:"", AvatarURL:"", HTMLURL:"", Name:"", Company:"", Blog:"", Location:"", Email:"", Description:"", PublicRepos:0, PublicGists:0, Followers:0, Following:0, TotalPrivateRepos:0, OwnedPrivateRepos:0, PrivateGists:0, DiskUsage:0, Collaborators:0, BillingEmail:"", Type:"", Plan:github.Plan{}, TwoFactorRequirementEnabled:false, DefaultRepoPermission:"", DefaultRepoSettings:"", MembersCanCreateRepos:false, MembersAllowedRepositoryCreationType:"", URL:"", EventsURL:"", HooksURL:"", IssuesURL:"", MembersURL:"", PublicMembersURL:"", ReposURL:""}`
 	if got := v.String(); got != want {
 		t.Errorf("Organization.String = %v, want %v", got, want)
 	}
diff --git a/github/orgs_test.go b/github/orgs_test.go
index 9fe484db..3b938fc9 100644
--- a/github/orgs_test.go
+++ b/github/orgs_test.go
@@ -93,6 +93,7 @@ func TestOrganizationsService_Get(t *testing.T) {
 
 	mux.HandleFunc("/orgs/o", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", mediaTypeMemberAllowedRepoCreationTypePreview)
 		fmt.Fprint(w, `{"id":1, "login":"l", "url":"u", "avatar_url": "a", "location":"l"}`)
 	})
 
diff --git a/github/teams_test.go b/github/teams_test.go
index 84514ee9..6210bb59 100644
--- a/github/teams_test.go
+++ b/github/teams_test.go
@@ -601,3 +601,104 @@ func TestTeamsService_RemoveTeamProject(t *testing.T) {
 		t.Errorf("Teams.RemoveTeamProject returned error: %v", err)
 	}
 }
+
+func TestTeamsService_ListIDPGroupsInOrganization(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/orgs/o/team-sync/groups", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", mediaTypeTeamSyncPreview)
+		testFormValues(t, r, values{
+			"page": "2",
+		})
+		fmt.Fprint(w, `{"groups": [{"group_id": "1",  "group_name": "n", "group_description": "d"}]}`)
+	})
+
+	opt := &ListOptions{Page: 2}
+	groups, _, err := client.Teams.ListIDPGroupsInOrganization(context.Background(), "o", opt)
+	if err != nil {
+		t.Errorf("Teams.ListIDPGroupsInOrganization returned error: %v", err)
+	}
+
+	want := &IDPGroupList{
+		Groups: []*IDPGroup{
+			{
+				GroupID:          String("1"),
+				GroupName:        String("n"),
+				GroupDescription: String("d"),
+			},
+		},
+	}
+	if !reflect.DeepEqual(groups, want) {
+		t.Errorf("Teams.ListIDPGroupsInOrganization returned %+v. want %+v", groups, want)
+	}
+}
+
+func TestTeamsService_ListIDPGroupsForTeam(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/teams/1/team-sync/group-mappings", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "GET")
+		testHeader(t, r, "Accept", mediaTypeTeamSyncPreview)
+		fmt.Fprint(w, `{"groups": [{"group_id": "1",  "group_name": "n", "group_description": "d"}]}`)
+	})
+
+	groups, _, err := client.Teams.ListIDPGroupsForTeam(context.Background(), "1")
+	if err != nil {
+		t.Errorf("Teams.ListIDPGroupsForTeam returned error: %v", err)
+	}
+
+	want := &IDPGroupList{
+		Groups: []*IDPGroup{
+			{
+				GroupID:          String("1"),
+				GroupName:        String("n"),
+				GroupDescription: String("d"),
+			},
+		},
+	}
+	if !reflect.DeepEqual(groups, want) {
+		t.Errorf("Teams.ListIDPGroupsForTeam returned %+v. want %+v", groups, want)
+	}
+}
+
+func TestTeamsService_CreateOrUpdateIDPGroupConnections(t *testing.T) {
+	client, mux, _, teardown := setup()
+	defer teardown()
+
+	mux.HandleFunc("/teams/1/team-sync/group-mappings", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, "PATCH")
+		testHeader(t, r, "Accept", mediaTypeTeamSyncPreview)
+		fmt.Fprint(w, `{"groups": [{"group_id": "1",  "group_name": "n", "group_description": "d"}]}`)
+	})
+
+	input := IDPGroupList{
+		Groups: []*IDPGroup{
+			{
+				GroupID:          String("1"),
+				GroupName:        String("n"),
+				GroupDescription: String("d"),
+			},
+		},
+	}
+
+	groups, _, err := client.Teams.CreateOrUpdateIDPGroupConnections(context.Background(), "1", input)
+	if err != nil {
+		t.Errorf("Teams.CreateOrUpdateIDPGroupConnections returned error: %v", err)
+	}
+
+	want := &IDPGroupList{
+		Groups: []*IDPGroup{
+			{
+				GroupID:          String("1"),
+				GroupName:        String("n"),
+				GroupDescription: String("d"),
+			},
+		},
+	}
+	if !reflect.DeepEqual(groups, want) {
+		t.Errorf("Teams.CreateOrUpdateIDPGroupConnections returned %+v. want %+v", groups, want)
+	}
+}